Skip to content

Commit

Permalink
Make BONSAI_ARCHIVE experimental for the first release. Add metrics. …
Browse files Browse the repository at this point in the history
…Use the term archive, not freezer

Signed-off-by: Matthew Whitehead <matthew1001@gmail.com>
  • Loading branch information
matthew1001 committed Oct 8, 2024
1 parent ee7c2fc commit 38cfdd9
Show file tree
Hide file tree
Showing 26 changed files with 371 additions and 314 deletions.
6 changes: 4 additions & 2 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -1939,8 +1939,10 @@ private PrivacyParameters privacyParameters() {
if (getDataStorageConfiguration().getDataStorageFormat() == DataStorageFormat.BONSAI) {
throw new ParameterException(commandLine, String.format("%s %s", "Bonsai", errorSuffix));
}
if (getDataStorageConfiguration().getDataStorageFormat() == DataStorageFormat.BONSAI_ARCHIVE) {
throw new ParameterException(commandLine, String.format("%s %s", "Bonsai archive", errorSuffix));
if (getDataStorageConfiguration().getDataStorageFormat()
== DataStorageFormat.X_BONSAI_ARCHIVE) {
throw new ParameterException(
commandLine, String.format("%s %s", "Bonsai archive", errorSuffix));
}

if (Boolean.TRUE.equals(privacyOptionGroup.isPrivacyMultiTenancyEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class DataStorageOptions implements CLIOptions<DataStorageConfiguration>
@Option(
names = {DATA_STORAGE_FORMAT},
description =
"Format to store trie data in. Either FOREST, BONSAI or BONSAI_ARCHIVE (default: ${DEFAULT-VALUE}).",
"Format to store trie data in. Either FOREST, BONSAI or X_BONSAI_ARCHIVE (default: ${DEFAULT-VALUE}).",
arity = "1")
private DataStorageFormat dataStorageFormat = DataStorageFormat.BONSAI;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void run() {
switch (dataStorageFormat) {
case FOREST -> 1;
case BONSAI -> 2;
case BONSAI_ARCHIVE -> 3;
case X_BONSAI_ARCHIVE -> 3;
};

@JsonSerialize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogPruner;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;

import java.io.IOException;
import java.io.PrintWriter;
Expand All @@ -39,7 +40,6 @@

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.hyperledger.besu.ethereum.worldstate.ImmutableDataStorageConfiguration;
import org.rocksdb.RocksDBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -323,7 +323,7 @@ private static TrieLogContext getTrieLogContext() {
final DataStorageConfiguration config = besuController.getDataStorageConfiguration();
checkArgument(
config.getDataStorageFormat().isBonsaiFormat(),
"Subcommand only works with data-storage-format=BONSAI or BONSAI_ARCHIVE");
"Subcommand only works with data-storage-format=BONSAI or X_BONSAI_ARCHIVE");

final StorageProvider storageProvider = besuController.getStorageProvider();
final BonsaiWorldStateKeyValueStorage rootWorldStateStorage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiWorldStateProvider;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.cache.BonsaiCachedMerkleTrieLoader;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiArchiveFreezer;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiArchiver;
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogManager;
import org.hyperledger.besu.ethereum.trie.diffbased.common.trielog.TrieLogPruner;
import org.hyperledger.besu.ethereum.trie.forest.ForestWorldStateArchive;
Expand Down Expand Up @@ -755,16 +755,17 @@ public BesuController build() {
}

// TODO - do we want a flag to turn this on and off?
if (DataStorageFormat.BONSAI_ARCHIVE.equals(dataStorageConfiguration.getDataStorageFormat())) {
if (DataStorageFormat.X_BONSAI_ARCHIVE.equals(
dataStorageConfiguration.getDataStorageFormat())) {
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage =
worldStateStorageCoordinator.getStrategy(BonsaiWorldStateKeyValueStorage.class);
final BonsaiArchiveFreezer archiveFreezer =
createBonsaiArchiveFreezer(
final BonsaiArchiver archiver =
createBonsaiArchiver(
worldStateKeyValueStorage,
blockchain,
scheduler,
((BonsaiWorldStateProvider) worldStateArchive).getTrieLogManager());
blockchain.observeBlockAdded(archiveFreezer);
blockchain.observeBlockAdded(archiver);
}

final List<Closeable> closeables = new ArrayList<>();
Expand Down Expand Up @@ -833,21 +834,22 @@ private TrieLogPruner createTrieLogPruner(
return trieLogPruner;
}

private BonsaiArchiveFreezer createBonsaiArchiveFreezer(
private BonsaiArchiver createBonsaiArchiver(
final WorldStateKeyValueStorage worldStateStorage,
final Blockchain blockchain,
final EthScheduler scheduler,
final TrieLogManager trieLogManager) {
final BonsaiArchiveFreezer archiveFreezer =
new BonsaiArchiveFreezer(
final BonsaiArchiver archiver =
new BonsaiArchiver(
(BonsaiWorldStateKeyValueStorage) worldStateStorage,
blockchain,
scheduler::executeServiceTask,
trieLogManager);
trieLogManager,
metricsSystem);

long archivedBlocks = archiveFreezer.initialize();
LOG.info("Bonsai archive initialised, caught up {} blocks", archivedBlocks);
return archiveFreezer;
long archivedBlocks = archiver.initialize();
LOG.info("Bonsai archiver initialised, caught up {} blocks", archivedBlocks);
return archiver;
}

/**
Expand Down Expand Up @@ -1143,7 +1145,7 @@ yield new BonsaiWorldStateProvider(
besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null),
evmConfiguration);
}
case BONSAI_ARCHIVE -> {
case X_BONSAI_ARCHIVE -> {
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage =
worldStateStorageCoordinator.getStrategy(BonsaiWorldStateKeyValueStorage.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,10 @@ public void privacyWithBonsaiExplicitMustError() {
@Test
public void privacyWithBonsaiArchiveExplicitMustError() {
// bypass overridden parseCommand method which specifies bonsai
super.parseCommand("--privacy-enabled", "--data-storage-format", "BONSAI_ARCHIVE");
super.parseCommand("--privacy-enabled", "--data-storage-format", "X_BONSAI_ARCHIVE");

assertThat(commandErrorOutput.toString(UTF_8))
.contains("Bonsai archive cannot be enabled with privacy.");
.contains("Bonsai archive cannot be enabled with privacy.");
assertThat(commandOutput.toString(UTF_8)).isEmpty();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
package org.hyperledger.besu.ethereum.storage.keyvalue;

import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.BONSAI;
import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.BONSAI_ARCHIVE;
import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.FOREST;
import static org.hyperledger.besu.plugin.services.storage.DataStorageFormat.X_BONSAI_ARCHIVE;

import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
Expand All @@ -31,20 +31,20 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
PRIVATE_TRANSACTIONS(new byte[] {3}),
PRIVATE_STATE(new byte[] {4}),
PRUNING_STATE(new byte[] {5}, EnumSet.of(FOREST)),
ACCOUNT_INFO_STATE(new byte[] {6}, EnumSet.of(BONSAI, BONSAI_ARCHIVE), false, true, false),
CODE_STORAGE(new byte[] {7}, EnumSet.of(BONSAI, BONSAI_ARCHIVE)),
ACCOUNT_STORAGE_STORAGE(new byte[] {8}, EnumSet.of(BONSAI, BONSAI_ARCHIVE), false, true, false),
TRIE_BRANCH_STORAGE(new byte[] {9}, EnumSet.of(BONSAI, BONSAI_ARCHIVE), false, true, false),
TRIE_LOG_STORAGE(new byte[] {10}, EnumSet.of(BONSAI, BONSAI_ARCHIVE), true, false, true),
ACCOUNT_INFO_STATE_FREEZER(
"ACCOUNT_INFO_STATE_FREEZER".getBytes(StandardCharsets.UTF_8),
EnumSet.of(BONSAI_ARCHIVE),
ACCOUNT_INFO_STATE(new byte[] {6}, EnumSet.of(BONSAI, X_BONSAI_ARCHIVE), false, true, false),
CODE_STORAGE(new byte[] {7}, EnumSet.of(BONSAI, X_BONSAI_ARCHIVE)),
ACCOUNT_STORAGE_STORAGE(new byte[] {8}, EnumSet.of(BONSAI, X_BONSAI_ARCHIVE), false, true, false),
TRIE_BRANCH_STORAGE(new byte[] {9}, EnumSet.of(BONSAI, X_BONSAI_ARCHIVE), false, true, false),
TRIE_LOG_STORAGE(new byte[] {10}, EnumSet.of(BONSAI, X_BONSAI_ARCHIVE), true, false, true),
ACCOUNT_INFO_STATE_ARCHIVE(
"ACCOUNT_INFO_STATE_ARCHIVE".getBytes(StandardCharsets.UTF_8),
EnumSet.of(X_BONSAI_ARCHIVE),
true,
false,
true),
ACCOUNT_STORAGE_FREEZER(
"ACCOUNT_STORAGE_FREEZER".getBytes(StandardCharsets.UTF_8),
EnumSet.of(BONSAI_ARCHIVE),
ACCOUNT_STORAGE_ARCHIVE(
"ACCOUNT_STORAGE_ARCHIVE".getBytes(StandardCharsets.UTF_8),
EnumSet.of(X_BONSAI_ARCHIVE),
true,
false,
true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static MutableWorldState createGenesisWorldState(
== DataStorageFormat.BONSAI) {
return createGenesisBonsaiWorldState(false);
} else if (Objects.requireNonNull(dataStorageConfiguration).getDataStorageFormat()
== DataStorageFormat.BONSAI_ARCHIVE) {
== DataStorageFormat.X_BONSAI_ARCHIVE) {
return createGenesisBonsaiWorldState(true);
} else {
return createGenesisForestWorldState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
package org.hyperledger.besu.ethereum.trie.diffbased.bonsai.storage.flat;

import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_FREEZER;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_FREEZER;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_INFO_STATE_ARCHIVE;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_ARCHIVE;
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.ACCOUNT_STORAGE_STORAGE;

import org.hyperledger.besu.datatypes.Hash;
Expand All @@ -25,8 +25,10 @@
import org.hyperledger.besu.ethereum.trie.NodeLoader;
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.flat.CodeStorageStrategy;
import org.hyperledger.besu.ethereum.trie.diffbased.common.storage.flat.FlatDbStrategy;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.plugin.data.BlockHeader;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.metrics.Counter;
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction;

Expand All @@ -46,12 +48,27 @@ public class ArchiveFlatDbStrategy extends FullFlatDbStrategy {
private final BonsaiContext context;
private static final Logger LOG = LoggerFactory.getLogger(ArchiveFlatDbStrategy.class);

protected final Counter getAccountFromArchiveCounter;
protected final Counter getStorageFromArchiveCounter;

public ArchiveFlatDbStrategy(
final BonsaiContext context,
final MetricsSystem metricsSystem,
final CodeStorageStrategy codeStorageStrategy) {
super(metricsSystem, codeStorageStrategy);
this.context = context;

getAccountFromArchiveCounter =
metricsSystem.createCounter(
BesuMetricCategory.BLOCKCHAIN,
"get_account_from_archive_counter",
"Total number of calls to get account that were from archived state");

getStorageFromArchiveCounter =
metricsSystem.createCounter(
BesuMetricCategory.BLOCKCHAIN,
"get_storage_from_archive_counter",
"Total number of calls to get storage that were from archived state");
}

static final byte[] MAX_BLOCK_SUFFIX = Bytes.ofUnsignedLong(Long.MAX_VALUE).toArrayUnsafe();
Expand Down Expand Up @@ -79,13 +96,17 @@ public Optional<Bytes> getFlatAccount(
.getNearestBefore(ACCOUNT_INFO_STATE, keyNearest)
.filter(found -> accountHash.commonPrefixLength(found.key()) >= accountHash.size());

// If there isn't a match look in the freezer DB segment
// If there isn't a match look in the archive DB segment
if (nearestAccount.isEmpty()) {
accountFound =
storage
.getNearestBefore(ACCOUNT_INFO_STATE_FREEZER, keyNearest)
.getNearestBefore(ACCOUNT_INFO_STATE_ARCHIVE, keyNearest)
.filter(found -> accountHash.commonPrefixLength(found.key()) >= accountHash.size())
.flatMap(SegmentedKeyValueStorage.NearestKeyValue::wrapBytes);

if (accountFound.isPresent()) {
getAccountFromArchiveCounter.inc();
}
} else {
accountFound =
nearestAccount
Expand All @@ -95,13 +116,12 @@ public Optional<Bytes> getFlatAccount(
DELETED_ACCOUNT_VALUE, found.value().orElse(DELETED_ACCOUNT_VALUE)))
// return empty when we find a "deleted value key"
.flatMap(SegmentedKeyValueStorage.NearestKeyValue::wrapBytes);
}

if (accountFound.isPresent()) {
// TODO - different metric for frozen lookups?
getAccountFoundInFlatDatabaseCounter.inc();
} else {
getAccountNotFoundInFlatDatabaseCounter.inc();
if (accountFound.isPresent()) {
getAccountFoundInFlatDatabaseCounter.inc();
} else {
getAccountNotFoundInFlatDatabaseCounter.inc();
}
}

return accountFound;
Expand Down Expand Up @@ -261,17 +281,21 @@ public Optional<Bytes> getFlatStorageValueByStorageSlotKey(
.filter(
found -> Bytes.of(naturalKey).commonPrefixLength(found.key()) >= naturalKey.length);

// If there isn't a match look in the freezer DB segment
// If there isn't a match look in the archive DB segment
if (nearestStorage.isEmpty()) {
// Check the frozen storage as old state is moved out of the primary DB segment
// Check the archived storage as old state is moved out of the primary DB segment
storageFound =
storage
.getNearestBefore(ACCOUNT_STORAGE_FREEZER, keyNearest)
.getNearestBefore(ACCOUNT_STORAGE_ARCHIVE, keyNearest)
// don't return accounts that do not have a matching account hash
.filter(
found ->
Bytes.of(naturalKey).commonPrefixLength(found.key()) >= naturalKey.length)
.flatMap(SegmentedKeyValueStorage.NearestKeyValue::wrapBytes);

if (storageFound.isPresent()) {
getStorageFromArchiveCounter.inc();
}
} else {
storageFound =
nearestStorage
Expand All @@ -284,7 +308,6 @@ public Optional<Bytes> getFlatStorageValueByStorageSlotKey(
.flatMap(SegmentedKeyValueStorage.NearestKeyValue::wrapBytes);

if (storageFound.isPresent()) {
// TODO - different metric for frozen lookups?
getStorageValueFlatDatabaseCounter.inc();
} else {
getStorageValueNotFoundInFlatDatabaseCounter.inc();
Expand Down
Loading

0 comments on commit 38cfdd9

Please sign in to comment.