-
Notifications
You must be signed in to change notification settings - Fork 893
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
Sync block body #8242
base: main
Are you sure you want to change the base?
Sync block body #8242
Conversation
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DownloadSyncBodiesStep.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SyncBlockBody.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/SyncBlockBody.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java
Outdated
Show resolved
Hide resolved
() -> new IllegalStateException("Blockchain is missing total difficulty data.")); | ||
difficultyForSyncing = parentTotalDifficulty.add(blockHeader.getDifficulty()); | ||
} else { | ||
difficultyForSyncing = difficultyForSyncing.add(blockHeader.getDifficulty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just an optimised path to avoid db lookup for the parent header?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as long as we are importing the blocks in order we shouldn't have to ask the DB what the last blocks td was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the parent header is already cached as we cache blocks when importing so this might not be needed
...m/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/CompleteSyncBlocksTask.java
Outdated
Show resolved
Hide resolved
.../src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetSyncBlocksFromPeerTask.java
Outdated
Show resolved
Hide resolved
.../src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadSyncReceiptsStep.java
Outdated
Show resolved
Hide resolved
.../src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadSyncReceiptsStep.java
Outdated
Show resolved
Hide resolved
.../eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/ImportSyncBlocksStep.java
Show resolved
Hide resolved
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all the sync tasks using the legacy peer task system? implementing the new peer task system for sync blocks could be done in another PR but we need at least the legacy version for now.
() -> new IllegalStateException("Blockchain is missing total difficulty data.")); | ||
difficultyForSyncing = parentTotalDifficulty.add(blockHeader.getDifficulty()); | ||
} else { | ||
difficultyForSyncing = difficultyForSyncing.add(blockHeader.getDifficulty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the parent header is already cached as we cache blocks when importing so this might not be needed
if ((header.getWithdrawalsRoot().isEmpty() && body.getWithdrawalsRoot() != null) | ||
|| (header.getWithdrawalsRoot().isPresent() && body.getWithdrawalsRoot() == null)) { | ||
return false; | ||
} else if ((header.getWithdrawalsRoot().isPresent() | ||
&& !header.getWithdrawalsRoot().get().equals(body.getWithdrawalsRoot()))) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think this could be simpler, we should be able to do the equality check on the optional to simplify this
i.e. header.getWithdrawalsRoot().equals(body.getWithdrawalsRoot())
?
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
final Hash blockAddedHash = recordBlockEvents(1).get(0).getBlock().getHash(); | ||
final Hash blockAddedHash = recordBlockEvents(1).get(0).getHeader().getHash(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably would have made for cleaner reviews if this kind of change was done separately
|
||
return BlockAddedEvent.createForSyncHeadAdvancement( | ||
newBlock.getHeader(), | ||
() -> new Block(newBlock.getHeader(), newBlock.getBody().getBodySupplier().get()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of delaying execution of getting the block body here?
final SyncBlockBody body = | ||
new SyncBlockBody( | ||
bytesCurrentBody, transactionBytes, ommersListBytes, withdrawalBytes, protocolSchedule); | ||
return body; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to just return new SyncBlockBody(...
?
final ArrayList<Bytes> bytesList = new ArrayList<>(withdrawals.size()); | ||
withdrawals.forEach(w -> bytesList.add(WithdrawalEncoder.encodeOpaqueBytes(w))); | ||
|
||
return Hash.wrap(trie.getRootHash()); | ||
return Util.getRootFromListOfBytes(bytesList); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could alternatively do return Util.getRootFromListOfBytes(withdrawals.stream().map(WithdrawalEncoder::encodeOpaqueBytes).toList());
to make it a simple one liner.
Likewise with the other changed methods in BodyValidation
boolean bodyMatchesHeader(final BlockBody body, final BlockHeader header) { | ||
if ((header.getWithdrawalsRoot().isEmpty() && body.getWithdrawals().isPresent()) | ||
|| (header.getWithdrawalsRoot().isPresent() && body.getWithdrawals().isEmpty())) { | ||
return false; | ||
} else if ((header.getWithdrawalsRoot().isPresent() | ||
&& !header | ||
.getWithdrawalsRoot() | ||
.get() | ||
.equals(BodyValidation.withdrawalsRoot(body.getWithdrawals().get())))) { | ||
return false; | ||
} else { | ||
return BodyValidation.transactionsRoot(body.getTransactions()) | ||
.equals(header.getTransactionsRoot()) | ||
&& BodyValidation.ommersHash(body.getOmmers()).equals(header.getOmmersHash()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not worth utilising the BlockBodyIdentifier class like in the other implementation?
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public abstract class AbstractDownloadReceiptsStep<B, BWR> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth adding some comments about exactly what B and BWR are expected to represent
Signed-off-by: stefan.pingel@consensys.net <stefan.pingel@consensys.net>
PR description
This PR changes the SNAP and CHECKPOINT sync to reduce the memory requirements by not decoding the block bodies requested from peers. This results in less memory and CPU usage during the sync.
This PR does make sure that the properties of the syncs are the same as before, e.g. transaction indexing and block added events haven't changed.
Note: this PR does not significantly reduce the sync time on it's own, because the bottleneck is the import step of the download pipeline. It will however allow us to increase the parallelism of the pipeline because of the reduced memory and CPU usage. Once the import step is improved that increase in parallelism will allow us to reduce the sync time.
Note: Not decoding receipts during the sync does improve the memory and CPU usage as well. This has been shown in a POC.
First screenshot:

CPU% GC young generation during sync with this PR:

CPU% GC young generation during sync without this PR:

Metrics during the sync: https://grafana.o11y.web3factory.consensys.net/d/_Mqt4ksnz/besu-full?from=2025-02-04T05:46:27.668Z&orgId=19&refresh=5s&timezone=utc&to=2025-02-05T19:09:47.777Z&var-Filters=&var-diskdevices=%5Ba-z%5D%2B%7Cnvme%5B0-9%5D%2Bn%5B0-9%5D%2B&var-executor=$__all&var-overview_engine_percentiles=$__all&var-protocol_inbound=$__all&var-protocol_outbound=$__all&var-sync_task_quantile=$__all&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-control-0005&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-control-0004&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-control-0003&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-control-0002&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-control-0001&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-0005&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-0004&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-0003&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-0002&var-system=dev-elc-bu-nb-mainnet-stefan-sync-withSBB-0001&var-txpool_layer=$__all&var-vertx_pool_name=$__all