-
Notifications
You must be signed in to change notification settings - Fork 835
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
Preload state trie node #4737
Preload state trie node #4737
Conversation
Signed-off-by: Karim TAAM <[email protected]>
Signed-off-by: Karim TAAM <[email protected]>
Signed-off-by: Karim TAAM <[email protected]>
3766ea2
to
bed1a9a
Compare
Signed-off-by: Karim TAAM <[email protected]>
bed1a9a
to
461ce45
Compare
Signed-off-by: Karim TAAM <[email protected]>
Signed-off-by: Karim TAAM <[email protected]>
e97d142
to
8d9b80c
Compare
…Prometheus. Signed-off-by: Ameziane H <[email protected]>
…Prometheus. Signed-off-by: Ameziane H <[email protected]>
Signed-off-by: Ameziane H <[email protected]>
Signed-off-by: Ameziane H <[email protected]>
Signed-off-by: Ameziane H <[email protected]>
Signed-off-by: Karim TAAM <[email protected]>
Signed-off-by: Ameziane H <[email protected]>
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiInMemoryWorldState.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiLayeredWorldState.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateArchive.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java
Outdated
Show resolved
Hide resolved
private final Cache<Bytes, Bytes> storageNodes; | ||
|
||
public OptimizedMerkleTrieLoader(final ObservableMetricsSystem metricsSystem) { | ||
accountsNodes = CacheBuilder.newBuilder().recordStats().maximumSize(ACCOUNT_CACHE_SIZE).build(); |
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.
Please avoid running complicated logic in a constructor. Maybe extract it into a factory method.
final BonsaiWorldStateKeyValueStorage worldStateStorage, | ||
final Hash worldStateRootHash, | ||
final Address account) { | ||
CompletableFuture.runAsync( |
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.
It feels to me weird that nobody handles the future when it is done. Maybe you can have the Optional that you were preloading to be a return value of the future and when the future completes, update your accountsNodes. That would also prevent the need of modifying a field in the middle of a function. Functions in functional programming should preferably not have side effects, so this might help.
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.
Good point, the idea was to execute asynchronously as much work as possible to fill the cache. These completable futures doesn't return any result, but I totally agree that it should be more clean to control the execution of the completable future.
This is a first version, and this can be improved in a future PR. The control of completable futures' executions will introduce a lot of changes that can be done in the future PR. We can for example stop all the futures when we start reading from the cache, this is not done in this current PR.
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/OptimizedMerkleTrieLoader.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/OptimizedMerkleTrieLoader.java
Outdated
Show resolved
Hide resolved
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/OptimizedMerkleTrieLoader.java
Outdated
Show resolved
Hide resolved
0315742
to
1562550
Compare
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/CachedMerkleTrieLoader.java
Fixed
Show fixed
Hide fixed
3b97e76
to
24b47be
Compare
Signed-off-by: Karim TAAM <[email protected]>
24b47be
to
24a792c
Compare
Signed-off-by: Karim TAAM <[email protected]>
a953258
to
eb01df4
Compare
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.
LGTM
Signed-off-by: Ameziane H <[email protected]>
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.
LGTM
@@ -116,7 +126,10 @@ protected Hash calculateRootHash( | |||
// next walk the account trie | |||
final StoredMerklePatriciaTrie<Bytes, Bytes> accountTrie = | |||
new StoredMerklePatriciaTrie<>( | |||
this::getAccountStateTrieNode, | |||
(location, hash) -> |
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.
it appears that Optional<Bytes> getAccountStateTrieNode()
is no longer used anywhere
@@ -300,8 +301,10 @@ public BesuController build() { | |||
reorgLoggingThreshold, | |||
dataDirectory.toString()); | |||
|
|||
final CachedMerkleTrieLoader cachedMerkleTrieLoader = new CachedMerkleTrieLoader(metricsSystem); |
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.
this feels like a worldstateArchive concern that is leaking. Non-blocking, but I think we could benefit from a builder and/or pass metricsSystem instead
import org.apache.tuweni.bytes.Bytes; | ||
import org.apache.tuweni.bytes.Bytes32; | ||
import org.apache.tuweni.units.bigints.UInt256; | ||
import org.jetbrains.annotations.NotNull; |
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 only intellij is going to use this annotation. Findbugs uses NonNull and would probably be a better choice.
} | ||
|
||
@VisibleForTesting | ||
public void cacheAccountNodes( |
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.
can be default visibility rather than public
} | ||
} | ||
|
||
public void preLoadStorageSlot( |
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.
can be default viz
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.
Non-blocking feedback, can be addressed in a subsequent PR.
|
||
import org.apache.tuweni.bytes.Bytes; | ||
import org.apache.tuweni.bytes.Bytes32; | ||
import org.apache.tuweni.units.bigints.UInt256; | ||
|
||
public class TrieGenerator { | ||
|
||
public static MerklePatriciaTrie<Bytes32, Bytes> generateTrie( | ||
public static MerklePatriciaTrie<Bytes, Bytes> generateTrie( |
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.
why the change from Bytes32 to Bytes for the MPTs ?
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.
because the keys are not necessarily only hashes. storage flat db has longer keys
worldStateRootHash, | ||
Function.identity(), | ||
Function.identity()); | ||
accountTrie.get(Hash.hash(account)); |
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.
we should comment that loading the cache is a side effect of calling get
final StoredMerklePatriciaTrie<Bytes, Bytes> accountTrie = | ||
new StoredMerklePatriciaTrie<>( | ||
(location, hash) -> { | ||
Optional<Bytes> node = worldStateStorage.getAccountStateTrieNode(location, hash); |
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 we are doing the ifPresent check inside the MPT rather than before we create it, like we do in cacheStorageNodes ?
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 sure I understand the comment. The if is different in this case unless I didn't understand what you mean 😄
I merge and I will fix in a next PR. Thank you for your reviews |
The idea behind this commit is to preload asynchronously account nodes and storage nodes from the database during the transaction processing to use these nodes during the calculate root hash step. We've created two caches, one for account nodes and one for storage nodes. The size of these caches is 100k for accounts and 200k for storage. We've tested other values but this configuration is the one that works better. We also use exporter cache metrics as Prometheus metrics to check cache efficiency. Signed-off-by: Karim TAAM <[email protected]> Co-authored-by: Ameziane H <[email protected]> Signed-off-by: Gabriel-Trintinalia <[email protected]>
The idea behind this commit is to preload asynchronously account nodes and storage nodes from the database during the transaction processing to use these nodes during the calculate root hash step. We've created two caches, one for account nodes and one for storage nodes. The size of these caches is 100k for accounts and 200k for storage. We've tested other values but this configuration is the one that works better. We also use exporter cache metrics as Prometheus metrics to check cache efficiency. Signed-off-by: Karim TAAM <[email protected]> Co-authored-by: Ameziane H <[email protected]> Signed-off-by: Sally MacFarlane <[email protected]>
The idea behind this commit is to preload asynchronously account nodes and storage nodes from the database during the transaction processing to use these nodes during the calculate root hash step. We've created two caches, one for account nodes and one for storage nodes. The size of these caches is 100k for accounts and 200k for storage. We've tested other values but this configuration is the one that works better. We also use exporter cache metrics as Prometheus metrics to check cache efficiency. Signed-off-by: Karim TAAM <[email protected]> Co-authored-by: Ameziane H <[email protected]>
Signed-off-by: Karim TAAM [email protected]
Co-authored-by: Ameziane H [email protected]
PR description
The idea behind this PR is to preload asynchronously account nodes and storage nodes from the database during the transaction processing to use these nodes during the calculate root hash step.
We've created two caches, one for account nodes and one for storage nodes. The size of these caches is 100k for accounts and 200k for storage. We've tested other values but this configuration is the one that works better.
We also exporter cache metrics as Prometheus metrics to check cache efficiency.
We didn't see any impact on GC activity even on 4 GiB Heaps (-Xmx4g).
The results
We did our tests on different AWS EC instances, here're the results.
M6a.xlarge (4 vCPU, 16 GiB)
Block processing time is 34% better for median values and 41% better for 95th percentile.
M5.xlarge (4 vCPU, 16 GiB)
Block processing time is 28% better for median values and 95th percentile.
I3.2xlarge (8 vCPU, 61 GiB)
Block processing time is 21% better for median values and 95th percentile.
Cache Efficiency
We can see in the screenshots below that these two caches are very efficient (>99%) and increasing storage cache size more than 200k is not necessary.
Account cache size = 100k and Storage cache size = 200k
Account cache size = 100k and Storage cache size = 1 million
Fixed Issue(s)
Documentation
doc-change-required
label to this PR ifupdates are required.
Changelog