Skip to content

Commit 1e084c7

Browse files
8337 modify chain data pruner (#8506)
* Add pre-merge pruning options to ChainPruningOptions Signed-off-by: Matilda Clerke <[email protected]> * Implement pre-merge block pruning in ChainDataPruner Signed-off-by: Matilda Clerke <[email protected]> * Add an info log to preMergePruningAction Signed-off-by: Matilda Clerke <[email protected]> * Add logging Signed-off-by: Matilda Clerke <[email protected]> * Fix database setup Signed-off-by: Matilda Clerke <[email protected]> * Unsubscribe after finishing pruning Signed-off-by: Matilda Clerke <[email protected]> * Enable garbage collection of blobs in static data segments Signed-off-by: Matilda Clerke <[email protected]> * Change start of pre-merge block tuning from 0 to 1 to preserve the genesis block Signed-off-by: Matilda Clerke <[email protected]> * Suggest garbage collection to try to avoid build up Signed-off-by: Matilda Clerke <[email protected]> * Fix missing space in option description in ChainPruningOptions.java Co-authored-by: Sally MacFarlane <[email protected]> Signed-off-by: Matilda-Clerke <[email protected]> * Fix missing space in option description in ChainPruningOptions.java Co-authored-by: Sally MacFarlane <[email protected]> Signed-off-by: Matilda-Clerke <[email protected]> * Rework ChainDataPruner changes Signed-off-by: Matilda Clerke <[email protected]> * Remove System.gc call Signed-off-by: Matilda Clerke <[email protected]> * Move merge block number into NetworkName Signed-off-by: Matilda Clerke <[email protected]> * Throttle pre-merge pruning progress logs to one per 5 minutes Signed-off-by: Matilda Clerke <[email protected]> * Allow different static data to have different garbage collection enabled status Signed-off-by: Matilda Clerke <[email protected]> * Update ChainDataPruner threadpool to match with metrics naming schemes Signed-off-by: Matilda Clerke <[email protected]> * Add 1 second sleep at start of pre-merge pruning action Signed-off-by: Matilda Clerke <[email protected]> * Keep transaction difficulty to be consistent with updated sync Signed-off-by: Matilda Clerke <[email protected]> --------- Signed-off-by: Matilda Clerke <[email protected]> Signed-off-by: Matilda-Clerke <[email protected]> Co-authored-by: Sally MacFarlane <[email protected]>
1 parent 25dc5d6 commit 1e084c7

File tree

7 files changed

+248
-54
lines changed

7 files changed

+248
-54
lines changed

besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -856,8 +856,6 @@ public void run() {
856856
// set merge config on the basis of genesis config
857857
setMergeConfigOptions();
858858

859-
setIgnorableStorageSegments();
860-
861859
instantiateSignatureAlgorithmFactory();
862860

863861
logger.info("Starting Besu");
@@ -869,6 +867,8 @@ public void run() {
869867

870868
configure();
871869

870+
setIgnorableStorageSegments();
871+
872872
// If we're not running against a named network, or if version compat protection has been
873873
// explicitly enabled, perform compatibility check
874874
VersionMetadata.versionCompatibilityChecks(versionCompatibilityProtection, dataDir());
@@ -2393,7 +2393,8 @@ private void setMergeConfigOptions() {
23932393

23942394
/** Set ignorable segments in RocksDB Storage Provider plugin. */
23952395
public void setIgnorableStorageSegments() {
2396-
if (!unstableChainPruningOptions.getChainDataPruningEnabled()) {
2396+
if (!unstableChainPruningOptions.getChainDataPruningEnabled()
2397+
&& !dataStorageConfiguration.getHistoryExpiryPruneEnabled()) {
23972398
rocksDBPlugin.addIgnorableSegmentIdentifier(KeyValueSegmentIdentifier.CHAIN_PRUNER_STATE);
23982399
}
23992400
}

besu/src/main/java/org/hyperledger/besu/cli/options/ChainPruningOptions.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration>
3030
private static final String CHAIN_PRUNING_BLOCKS_RETAINED_LIMIT_FLAG =
3131
"--Xchain-pruning-blocks-retained-limit";
3232
private static final String CHAIN_PRUNING_FREQUENCY_FLAG = "--Xchain-pruning-frequency";
33+
private static final String PRE_MERGE_PRUNING_QUANTITY_FLAG = "--Xpre-merge-pruning-quantity";
3334

3435
/**
3536
* The "CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED_LIMIT" field sets the minimum limit for the
@@ -42,6 +43,9 @@ public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration>
4243
/** The constant DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY. */
4344
public static final int DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY = 256;
4445

46+
/** The constant DEFAULT_PRE_MERGE_PRUNING_QUANTITY. */
47+
public static final int DEFAULT_PRE_MERGE_PRUNING_QUANTITY = 100;
48+
4549
@CommandLine.Option(
4650
hidden = true,
4751
names = {CHAIN_PRUNING_ENABLED_FLAG},
@@ -55,7 +59,7 @@ public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration>
5559
description =
5660
"The number of recent blocks for which to keep the chain data. Should be >= "
5761
+ CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED_LIMIT
58-
+ " (default: ${DEFAULT-VALUE})")
62+
+ " (default: ${DEFAULT-VALUE}). Unused if --Xhistory-expiry-prune is enabled")
5963
private final Long chainDataPruningBlocksRetained = CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED_LIMIT;
6064

6165
@CommandLine.Option(
@@ -65,7 +69,7 @@ public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration>
6569
"Allows setting the limit below which no more blocks can be pruned. This prevents setting a value lower than this for "
6670
+ CHAIN_PRUNING_BLOCKS_RETAINED_FLAG
6771
+ ". This flag should be used with caution as reducing the limit may have unintended side effects."
68-
+ " (default: ${DEFAULT-VALUE})")
72+
+ " (default: ${DEFAULT-VALUE}). Unused if --Xhistory-expiry-prune is enabled")
6973
private final Long chainDataPruningBlocksRetainedLimit =
7074
CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED_LIMIT;
7175

@@ -77,6 +81,14 @@ public class ChainPruningOptions implements CLIOptions<ChainPrunerConfiguration>
7781
private final PositiveNumber chainDataPruningBlocksFrequency =
7882
PositiveNumber.fromInt(DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY);
7983

84+
@CommandLine.Option(
85+
hidden = true,
86+
names = {PRE_MERGE_PRUNING_QUANTITY_FLAG},
87+
description =
88+
"The number of pre-merge blocks to prune per pruning operation. Must be non-negative (default: ${DEFAULT-VALUE})")
89+
private final PositiveNumber preMergePruningBlocksQuantity =
90+
PositiveNumber.fromInt(DEFAULT_PRE_MERGE_PRUNING_QUANTITY);
91+
8092
/** Default Constructor. */
8193
ChainPruningOptions() {}
8294

@@ -122,7 +134,8 @@ public ChainPrunerConfiguration toDomainObject() {
122134
chainDataPruningEnabled,
123135
chainDataPruningBlocksRetained,
124136
chainDataPruningBlocksRetainedLimit,
125-
chainDataPruningBlocksFrequency.getValue());
137+
chainDataPruningBlocksFrequency.getValue(),
138+
preMergePruningBlocksQuantity.getValue());
126139
}
127140

128141
@Override
@@ -135,6 +148,8 @@ public List<String> getCLIOptions() {
135148
CHAIN_PRUNING_BLOCKS_RETAINED_LIMIT_FLAG,
136149
chainDataPruningBlocksRetainedLimit.toString(),
137150
CHAIN_PRUNING_FREQUENCY_FLAG,
138-
chainDataPruningBlocksFrequency.toString());
151+
chainDataPruningBlocksFrequency.toString(),
152+
PRE_MERGE_PRUNING_QUANTITY_FLAG,
153+
preMergePruningBlocksQuantity.toString());
139154
}
140155
}

besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import java.util.Map;
117117
import java.util.Optional;
118118
import java.util.OptionalLong;
119+
import java.util.concurrent.atomic.AtomicLong;
119120
import java.util.concurrent.atomic.AtomicReference;
120121
import java.util.function.Supplier;
121122

@@ -683,14 +684,29 @@ public BesuController build() {
683684
final boolean fullSyncDisabled = !SyncMode.isFullSync(syncConfig.getSyncMode());
684685
final SyncState syncState = new SyncState(blockchain, ethPeers, fullSyncDisabled, checkpoint);
685686

686-
if (chainPrunerConfiguration.getChainPruningEnabled()) {
687-
final ChainDataPruner chainDataPruner = createChainPruner(blockchainStorage);
688-
blockchain.observeBlockAdded(chainDataPruner);
689-
LOG.info(
690-
"Chain data pruning enabled with recent blocks retained to be: "
691-
+ chainPrunerConfiguration.getChainPruningBlocksRetained()
692-
+ " and frequency to be: "
693-
+ chainPrunerConfiguration.getChainPruningBlocksFrequency());
687+
if (chainPrunerConfiguration.chainPruningEnabled()
688+
|| dataStorageConfiguration.getHistoryExpiryPruneEnabled()) {
689+
LOG.info("Adding ChainDataPruner to observe block added events");
690+
final AtomicLong chainDataPrunerObserverId = new AtomicLong();
691+
final ChainDataPruner chainDataPruner =
692+
createChainPruner(
693+
blockchainStorage,
694+
() -> blockchain.removeObserver(chainDataPrunerObserverId.get()),
695+
syncState);
696+
chainDataPrunerObserverId.set(blockchain.observeBlockAdded(chainDataPruner));
697+
if (chainPrunerConfiguration.chainPruningEnabled()) {
698+
LOG.info(
699+
"Chain data pruning enabled with recent blocks retained to be: "
700+
+ chainPrunerConfiguration.chainPruningBlocksRetained()
701+
+ " and frequency to be: "
702+
+ chainPrunerConfiguration.blocksFrequency());
703+
} else if (dataStorageConfiguration.getHistoryExpiryPruneEnabled()) {
704+
LOG.info(
705+
"Pre-merge block pruning enabled with frequency: "
706+
+ chainPrunerConfiguration.blocksFrequency()
707+
+ " and quantity: "
708+
+ chainPrunerConfiguration.preMergePruningBlocksQuantity());
709+
}
694710
}
695711

696712
final TransactionPool transactionPool =
@@ -1151,16 +1167,27 @@ yield new ForestWorldStateArchive(
11511167
};
11521168
}
11531169

1154-
private ChainDataPruner createChainPruner(final BlockchainStorage blockchainStorage) {
1170+
private ChainDataPruner createChainPruner(
1171+
final BlockchainStorage blockchainStorage,
1172+
final Runnable unsubscribeRunnable,
1173+
final SyncState syncState) {
11551174
return new ChainDataPruner(
11561175
blockchainStorage,
1176+
unsubscribeRunnable,
11571177
new ChainDataPrunerStorage(
11581178
storageProvider.getStorageBySegmentIdentifier(
11591179
KeyValueSegmentIdentifier.CHAIN_PRUNER_STATE)),
1160-
chainPrunerConfiguration.getChainPruningBlocksRetained(),
1161-
chainPrunerConfiguration.getChainPruningBlocksFrequency(),
1180+
syncState.getCheckpoint().map(Checkpoint::blockNumber).orElse(0L),
1181+
chainPrunerConfiguration.chainPruningEnabled()
1182+
? ChainDataPruner.Mode.CHAIN_PRUNING
1183+
: (dataStorageConfiguration.getHistoryExpiryPruneEnabled()
1184+
? ChainDataPruner.Mode.PRE_MERGE_PRUNING
1185+
: null),
1186+
chainPrunerConfiguration.chainPruningBlocksRetained(),
1187+
chainPrunerConfiguration.blocksFrequency(),
1188+
chainPrunerConfiguration.preMergePruningBlocksQuantity(),
11621189
MonitoredExecutors.newBoundedThreadPool(
1163-
ChainDataPruner.class.getSimpleName(),
1190+
EthScheduler.class.getSimpleName() + "-ChainDataPruner",
11641191
1,
11651192
1,
11661193
ChainDataPruner.MAX_PRUNING_THREAD_QUEUE_SIZE,

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainDataPruner.java

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,64 @@
1616

1717
import org.hyperledger.besu.datatypes.Hash;
1818
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;
19+
import org.hyperledger.besu.util.log.LogUtil;
1920

2021
import java.util.Collection;
2122
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.atomic.AtomicBoolean;
2224

2325
import org.slf4j.Logger;
2426
import org.slf4j.LoggerFactory;
2527

2628
public class ChainDataPruner implements BlockAddedObserver {
27-
public static final int MAX_PRUNING_THREAD_QUEUE_SIZE = 16;
2829
private static final Logger LOG = LoggerFactory.getLogger(ChainDataPruner.class);
30+
private static final int LOG_PRE_MERGE_PRUNING_PROGRESS_REPEAT_DELAY_SECONDS = 300;
31+
32+
public static final int MAX_PRUNING_THREAD_QUEUE_SIZE = 16;
33+
2934
private final BlockchainStorage blockchainStorage;
35+
private final Runnable unsubscribeRunnable;
3036
private final ChainDataPrunerStorage prunerStorage;
37+
private final long mergeBlock;
38+
private final Mode mode;
3139
private final long blocksToRetain;
3240
private final long pruningFrequency;
41+
private final long pruningQuantity;
3342
private final ExecutorService pruningExecutor;
43+
private final AtomicBoolean logPreMergePruningProgress = new AtomicBoolean(true);
3444

3545
public ChainDataPruner(
3646
final BlockchainStorage blockchainStorage,
47+
final Runnable unsubscribeRunnable,
3748
final ChainDataPrunerStorage prunerStorage,
49+
final long mergeBlock,
50+
final Mode mode,
3851
final long blocksToRetain,
3952
final long pruningFrequency,
53+
final long pruningQuantity,
4054
final ExecutorService pruningExecutor) {
4155
this.blockchainStorage = blockchainStorage;
56+
this.unsubscribeRunnable = unsubscribeRunnable;
4257
this.prunerStorage = prunerStorage;
58+
this.mergeBlock = mergeBlock;
59+
this.mode = mode;
4360
this.blocksToRetain = blocksToRetain;
4461
this.pruningFrequency = pruningFrequency;
4562
this.pruningExecutor = pruningExecutor;
63+
this.pruningQuantity = pruningQuantity;
4664
}
4765

4866
@Override
4967
public void onBlockAdded(final BlockAddedEvent event) {
68+
switch (mode) {
69+
case CHAIN_PRUNING -> chainPrunerAction(event);
70+
case PRE_MERGE_PRUNING -> {
71+
if (event.isNewCanonicalHead()) preMergePruningAction();
72+
}
73+
}
74+
}
75+
76+
private void chainPrunerAction(final BlockAddedEvent event) {
5077
final long blockNumber = event.getBlock().getHeader().getNumber();
5178
final long storedPruningMark = prunerStorage.getPruningMark().orElse(blockNumber);
5279
if (blockNumber < storedPruningMark) {
@@ -87,6 +114,56 @@ public void onBlockAdded(final BlockAddedEvent event) {
87114
});
88115
}
89116

117+
private void preMergePruningAction() {
118+
pruningExecutor.submit(
119+
() -> {
120+
try {
121+
Thread.sleep(1000);
122+
final long storedPruningMark = prunerStorage.getPruningMark().orElse(1L);
123+
final long expectedNewPruningMark =
124+
Math.min(storedPruningMark + pruningQuantity, mergeBlock);
125+
LOG.debug(
126+
"Attempting to prune blocks {} to {}", storedPruningMark, expectedNewPruningMark);
127+
final KeyValueStorageTransaction pruningTransaction = prunerStorage.startTransaction();
128+
final BlockchainStorage.Updater updater = blockchainStorage.updater();
129+
for (long blockNumber = storedPruningMark;
130+
blockNumber < expectedNewPruningMark;
131+
blockNumber++) {
132+
blockchainStorage
133+
.getBlockHash(blockNumber)
134+
.ifPresent(
135+
(blockHash) -> {
136+
updater.removeBlockBody(blockHash);
137+
updater.removeTransactionReceipts(blockHash);
138+
blockchainStorage
139+
.getBlockBody(blockHash)
140+
.ifPresent(
141+
blockBody ->
142+
blockBody
143+
.getTransactions()
144+
.forEach(
145+
t -> updater.removeTransactionLocation(t.getHash())));
146+
});
147+
}
148+
updater.commit();
149+
prunerStorage.setPruningMark(pruningTransaction, expectedNewPruningMark);
150+
pruningTransaction.commit();
151+
LOG.debug("Pruned pre-merge blocks up to {}", expectedNewPruningMark);
152+
LogUtil.throttledLog(
153+
() -> LOG.info("Pruned pre-merge blocks up to {}", expectedNewPruningMark),
154+
logPreMergePruningProgress,
155+
LOG_PRE_MERGE_PRUNING_PROGRESS_REPEAT_DELAY_SECONDS);
156+
if (expectedNewPruningMark == mergeBlock) {
157+
LOG.info("Done pruning pre-merge blocks.");
158+
LOG.debug("Unsubscribing from block added event observation");
159+
unsubscribeRunnable.run();
160+
}
161+
} catch (InterruptedException e) {
162+
throw new RuntimeException(e);
163+
}
164+
});
165+
}
166+
90167
private void pruneChainDataAtBlock(final KeyValueStorageTransaction tx, final long blockNumber) {
91168
final Collection<Hash> oldForkBlocks = prunerStorage.getForkBlocks(blockNumber);
92169
final BlockchainStorage.Updater updater = blockchainStorage.updater();
@@ -107,4 +184,9 @@ private void pruneChainDataAtBlock(final KeyValueStorageTransaction tx, final lo
107184
updater.commit();
108185
prunerStorage.removeForkBlocks(tx, blockNumber);
109186
}
187+
188+
public enum Mode {
189+
CHAIN_PRUNING,
190+
PRE_MERGE_PRUNING
191+
}
110192
}

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainPrunerConfiguration.java

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,12 @@
1414
*/
1515
package org.hyperledger.besu.ethereum.chain;
1616

17-
public class ChainPrunerConfiguration {
17+
public record ChainPrunerConfiguration(
18+
boolean chainPruningEnabled,
19+
long chainPruningBlocksRetained,
20+
long blocksFrequency,
21+
long chainPruningBlocksRetainedLimit,
22+
int preMergePruningBlocksQuantity) {
1823
public static final ChainPrunerConfiguration DEFAULT =
19-
new ChainPrunerConfiguration(false, 7200, 7200, 256);
20-
private final boolean enabled;
21-
private final long blocksRetained;
22-
private final long blocksFrequency;
23-
private final long blocksRetainedLimit;
24-
25-
public ChainPrunerConfiguration(
26-
final boolean enabled,
27-
final long blocksRetained,
28-
final long blocksRetainedLimit,
29-
final long blocksFrequency) {
30-
this.enabled = enabled;
31-
this.blocksRetained = blocksRetained;
32-
this.blocksRetainedLimit = blocksRetainedLimit;
33-
this.blocksFrequency = blocksFrequency;
34-
}
35-
36-
public long getChainPruningBlocksRetained() {
37-
return blocksRetained;
38-
}
39-
40-
public long getBlocksRetainedLimit() {
41-
return blocksRetainedLimit;
42-
}
43-
44-
public boolean getChainPruningEnabled() {
45-
return enabled;
46-
}
47-
48-
public long getChainPruningBlocksFrequency() {
49-
return blocksFrequency;
50-
}
24+
new ChainPrunerConfiguration(false, 7200, 7200, 256, 1000);
5125
}

0 commit comments

Comments
 (0)