Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove maybeHead from BackwardSyncContext #5497

Merged
merged 16 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Unite the tx-pool CLI options under the same Tx Pool Options group in UX. [#5466](https://github.com/hyperledger/besu/issues/5466)

### Bug Fixes
- Fix backwards sync bug where chain is rolled back too far, especially when restarting Nimbus [#5497](https://github.com/hyperledger/besu/pull/5497)

### Download Links

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ public Optional<BlockHeader> getOrSyncHeadByHash(final Hash headHash, final Hash
.setMessage("Appending new head block hash {} to backward sync")
.addArgument(headHash::toHexString)
.log();
backwardSyncContext.updateHead(headHash);
backwardSyncContext.maybeUpdateTargetHeight(headHash);
backwardSyncContext
.syncBackwardsUntil(headHash)
.thenRun(() -> updateFinalized(finalizedHash));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public String getName() {
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final FilterParameter filter = requestContext.getRequiredParameter(0, FilterParameter.class);
LOG.atTrace().setMessage("eth_getLogs FilterParameter: {}").addArgument(filter).log();

if (!filter.isValid()) {
return new JsonRpcErrorResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public static GetBodiesFromPeerTask forHeaders(
protected PendingPeerRequest sendRequest() {
final List<Hash> blockHashes =
headers.stream().map(BlockHeader::getHash).collect(Collectors.toList());
LOG.atTrace()
.setMessage("Requesting {} bodies with hashes {}.")
.addArgument(blockHashes.size())
.addArgument(blockHashes)
.log();
final long minimumRequiredBlockNumber = headers.get(headers.size() - 1).getNumber();

return sendRequestToPeer(
Expand Down Expand Up @@ -128,6 +133,13 @@ protected Optional<List<Block>> processResponse(
// Clear processed headers
headers.clear();
}
LOG.atTrace()
.setMessage("Associated {} bodies with {} headers to get {} blocks with these hashes: {}")
.addArgument(bodies.size())
.addArgument(headers.size())
.addArgument(blocks.size())
.addArgument(blocks.stream().map(Block::getHash).collect(Collectors.toList()))
siladu marked this conversation as resolved.
Show resolved Hide resolved
.log();
return Optional.of(blocks);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ protected CompletableFuture<List<BlockHeader>> executeTaskOnCurrentPeer(
return executeSubTask(task::run)
.thenApply(
peerResult -> {
LOG.debug(
"Get {} block headers by hash {} from peer {} has result {}",
LOG.trace(
"Got {} block headers by hash {} from peer {} has result {}",
count,
referenceHash,
currentPeer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ public class BackwardSyncContext {
private final AtomicReference<Status> currentBackwardSyncStatus = new AtomicReference<>();
private final BackwardChain backwardChain;
private int batchSize = BATCH_SIZE;
private Optional<Hash> maybeHead = Optional.empty();
private final int maxRetries;
private final long millisBetweenRetries = DEFAULT_MILLIS_BETWEEN_RETRIES;
private final Subscribers<BadChainListener> badChainListeners = Subscribers.create();
Expand Down Expand Up @@ -101,17 +100,21 @@ public synchronized boolean isSyncing() {
.orElse(Boolean.FALSE);
}

public synchronized void updateHead(final Hash headHash) {
if (Hash.ZERO.equals(headHash)) {
maybeHead = Optional.empty();
} else {
maybeHead = Optional.of(headHash);
public synchronized void maybeUpdateTargetHeight(final Hash headHash) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename method to reflect what is still does after removing maybeHead

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am not wrong this is redundant, and the method can be removed, since the update of the target height is done in the syncBackwardsUntil method, that is called just after

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I will consider removing this and possiblyMoveHead in a subsequent PR. I want to study the code a bit more and don't want to invalidate this PR's testing.

if (!Hash.ZERO.equals(headHash)) {
Optional<Status> maybeCurrentStatus = Optional.ofNullable(currentBackwardSyncStatus.get());
maybeCurrentStatus.ifPresent(
status ->
backwardChain
.getBlock(headHash)
.ifPresent(block -> status.updateTargetHeight(block.getHeader().getNumber())));
.ifPresent(
block -> {
LOG.atTrace()
.setMessage("updateTargetHeight to {}")
.addArgument(block.getHeader().getNumber())
siladu marked this conversation as resolved.
Show resolved Hide resolved
.log();
status.updateTargetHeight(block.getHeader().getNumber());
}));
}
}

Expand Down Expand Up @@ -331,20 +334,9 @@ protected Void saveBlock(final Block block) {
@VisibleForTesting
protected void possiblyMoveHead(final Block lastSavedBlock) {
final MutableBlockchain blockchain = getProtocolContext().getBlockchain();
if (maybeHead.isEmpty()) {
LOG.debug("Nothing to do with the head");
return;
}

final Hash head = maybeHead.get();
if (blockchain.getChainHead().getHash().equals(head)) {
LOG.debug("Head is already properly set");
return;
}

if (blockchain.contains(head)) {
LOG.debug("Changing head to {}", head);
blockchain.rewindToBlock(head);
if (blockchain.getChainHead().getHash().equals(lastSavedBlock.getHash())) {
Copy link
Contributor Author

@siladu siladu Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of maybeHead, I am checking the passed in hash (which should originate from the FCU). I'm not sure if this check is useful at all now though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also removed the blockchain.contains(hash) check. Any reason for that? Could possiblyMoveHead be called before we save the block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously it was only acting on maybeHead and that is now removed. There wasn't an equivalent check for lastSavedBlock.

possiblyMoveHead is currently only called immediately after appending the block to the chain

this.getProtocolContext()
.getBlockchain()
.appendBlock(block, optResult.getYield().get().getReceipts());
possiblyMoveHead(block);

I'm wondering whether to remove possiblyMoveHead entirely now as I'm not sure we would ever rewind to the last saved block (should always match this if condition I think)...
Would be good to get a second opinion from @fab-10

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code possiblyMoveHead could be removed, but I am still if thinking if this method was introduced to manage cases when there was a reorg, and still do not have a full answer, anyway the amount of tests that you have done make me confident

LOG.debug("Head is already properly set to {}", lastSavedBlock.toLogString());
siladu marked this conversation as resolved.
Show resolved Hide resolved
return;
}

Expand Down Expand Up @@ -442,10 +434,6 @@ public boolean progressLogDue() {
return false;
}

public CompletableFuture<Void> getCurrentFuture() {
return currentFuture;
}

public long getTargetChainHeight() {
return targetChainHeight;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import java.util.concurrent.ExecutionException;
import javax.annotation.Nonnull;

import org.apache.tuweni.bytes.Bytes;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -256,13 +257,45 @@ private Block getBlockByNumber(final int number) {
}

@Test
public void testUpdatingHead() {
context.updateHead(localBlockchain.getBlockByNumber(4).orElseThrow().getHash());
context.possiblyMoveHead(null);
public void shouldMoveHead() {
final Block lastSavedBlock = localBlockchain.getBlockByNumber(4).orElseThrow();
context.possiblyMoveHead(lastSavedBlock);

assertThat(localBlockchain.getChainHeadBlock().getHeader().getNumber()).isEqualTo(4);
}

@Test
public void shouldNotMoveHeadWhenAlreadyHead() {
final Block lastSavedBlock = localBlockchain.getBlockByNumber(25).orElseThrow();
context.possiblyMoveHead(lastSavedBlock);

assertThat(localBlockchain.getChainHeadBlock().getHeader().getNumber()).isEqualTo(25);
}

@Test
public void shouldUpdateTargetHeightWhenStatusPresent() {
// Given
BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
when(blockHeader.getParentHash()).thenReturn(Hash.fromHexStringLenient("0x41"));
when(blockHeader.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
when(blockHeader.getNumber()).thenReturn(42L);
Block unknownBlock = Mockito.mock(Block.class);
when(unknownBlock.getHeader()).thenReturn(blockHeader);
when(unknownBlock.getHash()).thenReturn(Hash.fromHexStringLenient("0x42"));
when(unknownBlock.toRlp()).thenReturn(Bytes.EMPTY);
context.syncBackwardsUntil(unknownBlock); // set the status
assertThat(context.getStatus().getTargetChainHeight()).isEqualTo(42);
final Hash backwardChainHash =
remoteBlockchain.getBlockByNumber(LOCAL_HEIGHT + 4).get().getHash();
final Block backwardChainBlock = backwardChain.getTrustedBlock(backwardChainHash);

// When
context.maybeUpdateTargetHeight(backwardChainBlock.getHash());

// Then
assertThat(context.getStatus().getTargetChainHeight()).isEqualTo(29);
}

@Test
public void shouldProcessExceptionsCorrectly() {
assertThatThrownBy(
Expand Down