From 4b7f2ae0e3bab8a6cf4ca9e76b474275080839bb Mon Sep 17 00:00:00 2001 From: lailiuyuan <91760130+lailiuyuan@users.noreply.github.com> Date: Mon, 4 Oct 2021 20:41:01 -0700 Subject: [PATCH] #1851 FastSyncDownloader should stop processing after receiving shutdown signal (#2838) Signed-off-by: Liu-yuan Lai Co-authored-by: Sally MacFarlane --- .../eth/sync/fastsync/FastSyncDownloader.java | 8 +++- .../sync/fastsync/FastSyncDownloaderTest.java | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java index 9b519d25d2..731efbca38 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java @@ -88,11 +88,14 @@ public class FastSyncDownloader { private CompletableFuture handleFailure(final Throwable error) { trailingPeerRequirements = Optional.empty(); - if (ExceptionUtils.rootCause(error) instanceof FastSyncException) { + Throwable rootCause = ExceptionUtils.rootCause(error); + if (rootCause instanceof FastSyncException) { return CompletableFuture.failedFuture(error); - } else if (ExceptionUtils.rootCause(error) instanceof StalledDownloadException) { + } else if (rootCause instanceof StalledDownloadException) { LOG.info("Re-pivoting to newer block."); return start(FastSyncState.EMPTY_SYNC_STATE); + } else if (rootCause instanceof CancellationException) { + return CompletableFuture.failedFuture(error); } else { LOG.error( "Encountered an unexpected error during fast sync. Restarting fast sync in " @@ -107,6 +110,7 @@ public class FastSyncDownloader { public void stop() { synchronized (this) { if (running.compareAndSet(true, false)) { + LOG.info("Stopping fast sync"); // Cancelling the world state download will also cause the chain download to be cancelled. worldStateDownloader.cancel(); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java index ee282c5600..17182c8da3 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java @@ -16,7 +16,9 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +37,8 @@ import org.hyperledger.besu.services.tasks.TaskCollection; import java.nio.file.Path; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.assertj.core.api.Assertions; @@ -228,6 +232,41 @@ public class FastSyncDownloaderTest { assertThat(worldStateFuture).isCancelled(); } + @Test + public void shouldAbortIfStopped() { + final FastSyncState selectPivotBlockState = new FastSyncState(50); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = new FastSyncState(pivotBlockHeader); + when(fastSyncActions.waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE)).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE)) + .thenReturn(completedFuture(selectPivotBlockState)); + doAnswer( + invocation -> { + CompletableFuture future = new CompletableFuture<>(); + Executors.newSingleThreadScheduledExecutor() + .schedule( + () -> future.complete(downloadPivotBlockHeaderState), + 500, + TimeUnit.MILLISECONDS); + return future; + }) + .when(fastSyncActions) + .downloadPivotBlockHeader(selectPivotBlockState); + + final CompletableFuture result = downloader.start(); + downloader.stop(); + + Throwable thrown = catchThrowable(() -> result.get()); + assertThat(thrown).hasCauseExactlyInstanceOf(CancellationException.class); + + verify(fastSyncActions).waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions).selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(storage).storeState(downloadPivotBlockHeaderState); + verify(worldStateDownloader).cancel(); + verifyNoMoreInteractions(fastSyncActions, worldStateDownloader, storage); + } + @Test public void shouldNotConsiderFastSyncCompleteIfOnlyWorldStateDownloadIsComplete() { final CompletableFuture chainFuture = new CompletableFuture<>();