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

Add chain data pruning experimental feature #4686

Merged
merged 40 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
fce33c1
Add chain pruner
wcgcyx Nov 11, 2022
be8f081
Add tests and options
wcgcyx Nov 15, 2022
af59579
Add pruning frequency
wcgcyx Nov 16, 2022
16de0e9
Fix unit tests
wcgcyx Nov 17, 2022
3e67042
Fix chain pruning tests
wcgcyx Nov 17, 2022
0d433a3
Increase minimum blocks to retain
wcgcyx Nov 18, 2022
0e545c5
Skip ancestor check in pruning mode
wcgcyx Nov 21, 2022
4e0e627
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 21, 2022
9313b02
Insert changelog entry & fix build issue
wcgcyx Nov 21, 2022
3e3596b
Fix unit test
wcgcyx Nov 21, 2022
d0807ff
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 21, 2022
4c1d792
Fix changelog & address reviews
wcgcyx Nov 22, 2022
95e86a5
Fix minor issues & code cleanup
wcgcyx Nov 24, 2022
a63542a
Separate class for pruning storage
wcgcyx Nov 24, 2022
580942a
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 24, 2022
328005b
Rename options
wcgcyx Nov 25, 2022
c34154f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 25, 2022
64ba456
Move pruning to separate thread
wcgcyx Nov 29, 2022
a42a933
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Nov 29, 2022
322b327
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 1, 2022
cd37f0f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 2, 2022
ac79611
Update changelog
wcgcyx Dec 2, 2022
51c6c6d
Remove global variable & decrease minimum allowed retaining block
wcgcyx Dec 5, 2022
96c2934
Limit total pruning threads
wcgcyx Dec 5, 2022
cbc460b
Update pruner constructor
wcgcyx Dec 5, 2022
99b273f
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 5, 2022
cc4648e
Update besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
wcgcyx Dec 6, 2022
8315393
Spotless & Remove unnecessary comments
wcgcyx Dec 6, 2022
5992b66
More clear variable
wcgcyx Dec 6, 2022
5d4da4e
Clear logging
wcgcyx Dec 6, 2022
118178e
Fix bug & use monitored executors
wcgcyx Dec 6, 2022
91102a7
New domain object
wcgcyx Dec 7, 2022
962adb6
Fix tests
wcgcyx Dec 7, 2022
1368c55
Move flag to merge context
wcgcyx Dec 7, 2022
d41661b
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 7, 2022
2a439f1
Renaming
wcgcyx Dec 8, 2022
d29a286
Rename tx
wcgcyx Dec 9, 2022
e2c088c
Merge branch 'main' into 4476-chain-data-pruning
wcgcyx Dec 9, 2022
25d514d
Fix issues
wcgcyx Dec 9, 2022
571a022
Merge branch 'main' into 4476-chain-data-pruning
jframe Dec 12, 2022
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 @@ -21,6 +21,7 @@
- Remove some log statements that are keeping some objects live in heap for a long time, to reduce the amount of memory required during initial sync [#4705](https://github.com/hyperledger/besu/pull/4705)
- Add field `type` to Transaction receipt object (eth_getTransactionReceipt) [#4505](https://github.com/hyperledger/besu/issues/4505)
- Print an overview of configuration and system information at startup [#4451](https://github.com/hyperledger/besu/pull/4451)
- Add chain data pruning feature with three experimental CLI options: `--Xchain-data-pruning-enabled`, `--Xchain-data-pruning-blocks-retained` and `--Xchain-data-pruning-frequency` [#4686](https://github.com/hyperledger/besu/pull/4686)

### Bug Fixes

Expand Down
24 changes: 23 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.hyperledger.besu.cli.options.stable.LoggingLevelOption;
import org.hyperledger.besu.cli.options.stable.NodePrivateKeyFileOption;
import org.hyperledger.besu.cli.options.stable.P2PTLSConfigOptions;
import org.hyperledger.besu.cli.options.unstable.ChainDataPruningOptions;
import org.hyperledger.besu.cli.options.unstable.DnsOptions;
import org.hyperledger.besu.cli.options.unstable.EthProtocolOptions;
import org.hyperledger.besu.cli.options.unstable.EvmOptions;
Expand Down Expand Up @@ -288,6 +289,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final PrivacyPluginOptions unstablePrivacyPluginOptions = PrivacyPluginOptions.create();
private final EvmOptions unstableEvmOptions = EvmOptions.create();
private final IpcOptions unstableIpcOptions = IpcOptions.create();
private final ChainDataPruningOptions unstableChainDataPruningOptions =
ChainDataPruningOptions.create();

// stable CLI options
private final DataStorageOptions dataStorageOptions = DataStorageOptions.create();
Expand Down Expand Up @@ -1532,6 +1535,7 @@ private void handleUnstableOptions() {
.put("Launcher", unstableLauncherOptions)
.put("EVM Options", unstableEvmOptions)
.put("IPC Options", unstableIpcOptions)
.put("Chain Data Pruning Options", unstableChainDataPruningOptions)
.build();

UnstableOptionsSubCommand.createUnstableOptions(commandLine, unstableOptions);
Expand Down Expand Up @@ -1761,6 +1765,7 @@ private void validateOptions() {
validateDnsOptionsParams();
ensureValidPeerBoundParams();
validateRpcOptionsParams();
validateChainDataPruningParams();
p2pTLSConfigOptions.checkP2PTLSOptionsDependencies(logger, commandLine);
pkiBlockCreationOptions.checkPkiBlockCreationOptionsDependencies(logger, commandLine);
}
Expand Down Expand Up @@ -1912,6 +1917,18 @@ public void validateRpcOptionsParams() {
}
}

public void validateChainDataPruningParams() {
siladu marked this conversation as resolved.
Show resolved Hide resolved
if (unstableChainDataPruningOptions.getChainDataPruningEnabled()) {
if (unstableChainDataPruningOptions.getChainDataPruningBlocksRetained()
< ChainDataPruningOptions.DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED) {
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
throw new ParameterException(
this.commandLine,
"--Xchain-data-pruning-blocks-retained must be >= "
+ ChainDataPruningOptions.DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED);
}
}
}

private GenesisConfigOptions readGenesisConfigOptions() {

try {
Expand Down Expand Up @@ -2169,7 +2186,12 @@ public BesuControllerBuilder getControllerBuilder() {
.reorgLoggingThreshold(reorgLoggingThreshold)
.evmConfiguration(unstableEvmOptions.toDomainObject())
.dataStorageConfiguration(dataStorageOptions.toDomainObject())
.maxPeers(p2PDiscoveryOptionGroup.maxPeers);
.maxPeers(p2PDiscoveryOptionGroup.maxPeers)
.isChainDataPruningEnabled(unstableChainDataPruningOptions.getChainDataPruningEnabled())
.chainDataPruningBlocksRetained(
unstableChainDataPruningOptions.getChainDataPruningBlocksRetained())
.chainDataPruningFrequency(
unstableChainDataPruningOptions.getChainDataPruningBlocksFrequency());
Copy link
Contributor

Choose a reason for hiding this comment

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

Can domain object be created instead so that 3 values don't need to passed through

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

}

private GraphQLConfiguration graphQLConfiguration() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.cli.options.unstable;

import org.hyperledger.besu.util.number.PositiveNumber;

import picocli.CommandLine;

public class ChainDataPruningOptions {

public static final long DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED = 50400;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to avoid this magic value? If syncing takes longer than a week then this will fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, ideally if the pruner has the syncing context to know the current pivot block and if the syncing has finished, then it can avoid pruning beyond the pivot block before syncing is completed. Do you think we should do it in a separate ticket?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should do this in another ticket.

Copy link
Contributor

Choose a reason for hiding this comment

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

Rename to MAX_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED? This not just the default but also the maximum number of blocks that will be pruned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually the minimum value for the blocks to retain. I've also changed it to be much smaller.

public static final int DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY = 256;

@CommandLine.Option(
hidden = true,
names = {"--Xchain-data-pruning-enabled"},
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
description =
"Enable the chain pruner to actively prune old chain data (default: ${DEFAULT-VALUE})")
private final Boolean chainDataPruningEnabled = Boolean.FALSE;

@CommandLine.Option(
hidden = true,
names = {"--Xchain-data-pruning-blocks-retained"},
description =
"The number of recent blocks for which to keep the chain data. Must be >= "
+ DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED
+ " (default: ${DEFAULT-VALUE})")
private final Long chainDataPruningBlocksRetained =
DEFAULT_CHAIN_DATA_PRUNING_MIN_BLOCKS_RETAINED;

@CommandLine.Option(
hidden = true,
names = {"--Xchain-data-pruning-frequency"},
description =
"The number of blocks added to the chain between two pruning operations. Must be non-negative (default: ${DEFAULT-VALUE})")
private final PositiveNumber chainDataPruningBlocksFrequency =
PositiveNumber.fromInt(DEFAULT_CHAIN_DATA_PRUNING_FREQUENCY);

public static ChainDataPruningOptions create() {
return new ChainDataPruningOptions();
}

public Boolean getChainDataPruningEnabled() {
return chainDataPruningEnabled;
}

public Long getChainDataPruningBlocksRetained() {
return chainDataPruningBlocksRetained;
}

public Long getChainDataPruningBlocksFrequency() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason this type doesn't match the PositiveNumber type above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converting and returning long makes it easy to compare with block number (also in long) in use of outside. Using Positive number is to ensure user can only supply positive frequency.

return (long) chainDataPruningBlocksFrequency.getValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.BlockchainStorage;
import org.hyperledger.besu.ethereum.chain.ChainDataPruner;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
Expand Down Expand Up @@ -139,6 +140,9 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
Collections.emptyList();
protected EvmConfiguration evmConfiguration;
protected int maxPeers;
protected boolean isChainDataPruningEnabled;
protected long chainDataPruningBlocksRetained;
protected long chainDataPruningFrequency;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could maybe wrap in a ChainDataPruningConfiguration object?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


public BesuControllerBuilder storageProvider(final StorageProvider storageProvider) {
this.storageProvider = storageProvider;
Expand Down Expand Up @@ -267,6 +271,22 @@ public BesuControllerBuilder maxPeers(final int maxPeers) {
return this;
}

public BesuControllerBuilder isChainDataPruningEnabled(final boolean isChainDataPruningEnabled) {
this.isChainDataPruningEnabled = isChainDataPruningEnabled;
return this;
}

public BesuControllerBuilder chainDataPruningBlocksRetained(
final long chainDataPruningBlocksRetained) {
this.chainDataPruningBlocksRetained = chainDataPruningBlocksRetained;
return this;
}

public BesuControllerBuilder chainDataPruningFrequency(final long chainDataPruningFrequency) {
this.chainDataPruningFrequency = chainDataPruningFrequency;
return this;
}

public BesuController build() {
checkNotNull(genesisConfig, "Missing genesis config");
checkNotNull(syncConfig, "Missing sync config");
Expand Down Expand Up @@ -300,6 +320,23 @@ public BesuController build() {
reorgLoggingThreshold,
dataDirectory.toString());

if (isChainDataPruningEnabled) {
ChainDataPruner.enablePruning();
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
final ChainDataPruner chainDataPruner =
new ChainDataPruner(
blockchainStorage,
storageProvider.getStorageBySegmentIdentifier(
KeyValueSegmentIdentifier.CHAIN_PRUNER_STATE),
chainDataPruningBlocksRetained,
chainDataPruningFrequency);
blockchain.observeBlockAdded(chainDataPruner);
LOG.info(
"Chain data pruning enabled with recent blocks retained to be: "
+ chainDataPruningBlocksRetained
+ " and frequency to be: "
+ chainDataPruningFrequency);
}

final WorldStateArchive worldStateArchive =
createWorldStateArchive(worldStateStorage, blockchain);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ public void initMocks() throws Exception {
when(mockControllerBuilder.dataStorageConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.evmConfiguration(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.maxPeers(anyInt())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.isChainDataPruningEnabled(anyBoolean()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.chainDataPruningBlocksRetained(anyLong()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.chainDataPruningFrequency(anyLong()))
.thenReturn(mockControllerBuilder);
// doReturn used because of generic BesuController
doReturn(mockController).when(mockControllerBuilder).build();
lenient().when(mockController.getProtocolManager()).thenReturn(mockEthProtocolManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineUpdateForkchoiceResult;
import org.hyperledger.besu.ethereum.chain.ChainDataPruner;
import org.hyperledger.besu.ethereum.core.BlockHeader;

import java.util.Optional;
Expand Down Expand Up @@ -116,7 +117,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup, this should be unnecessary after merge
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newHead.get())) {
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newHead.get())
&& !ChainDataPruner.isPruningEnabled()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be nicer to drive this based on the chain data pruning enabled flag rather than setting and using a static global field

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, I tried that a bit but it seems like that would involve bringing besu command into the protocol context. I consider it to be a temporary solution until #4703 is merged. Then, we could just do a cleanup to get rid of this global field completely.

Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to bring the entire Besu command into the protocol context. At most, the chain pruning config values would need to be passed through. A compromise could be to make the ChainPruner a singleton instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, It seems cleaner to add a flag in merge context instead, following what @gfukushima did in #4735

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated to have a temporary flag in protocol context instead. Global variable gets removed.

logForkchoiceUpdatedCall(INVALID, forkChoice);
return new JsonRpcSuccessResponse(
requestId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EnginePayloadStatusResult;
import org.hyperledger.besu.ethereum.chain.ChainDataPruner;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
Expand Down Expand Up @@ -194,7 +195,8 @@ public JsonRpcResponse syncResponse(final JsonRpcRequestContext requestContext)
}

// TODO: post-merge cleanup
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)) {
if (!mergeCoordinator.latestValidAncestorDescendsFromTerminal(newBlockHeader)
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
&& !ChainDataPruner.isPruningEnabled()) {
mergeCoordinator.addBadBlock(block, Optional.empty());
return respondWithInvalid(
reqId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ interface Updater {

void removeBlockHash(long blockNumber);

void removeBlockHeader(final Hash blockHash);

void removeBlockBody(final Hash blockHash);

void removeTransactionReceipts(final Hash blockHash);

void removeTransactionLocation(Hash transactionHash);

void removeTotalDifficulty(final Hash blockHash);

void commit();

void rollback();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.chain;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction;

import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ChainDataPruner implements BlockAddedObserver {
private static final Logger LOG = LoggerFactory.getLogger(ChainDataPruner.class);

// TODO: cleanup - pruningEnabled will not be required after
// https://github.com/hyperledger/besu/pull/4703 is merged.
private static boolean pruningEnabled = false;
private final BlockchainStorage blockchainStorage;
private final ChainDataPrunerStorage prunerStorage;
private final long blocksToRetain;

private final long pruningFrequency;

public ChainDataPruner(
final BlockchainStorage blockchainStorage,
final KeyValueStorage storage,
final long blocksToRetain,
final long pruningFrequency) {
this.blockchainStorage = blockchainStorage;
this.prunerStorage = new ChainDataPrunerStorage(storage);
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
this.blocksToRetain = blocksToRetain;
this.pruningFrequency = pruningFrequency;
}

public static void enablePruning() {
pruningEnabled = true;
}

public static boolean isPruningEnabled() {
return pruningEnabled;
}

@Override
public void onBlockAdded(final BlockAddedEvent event) {
siladu marked this conversation as resolved.
Show resolved Hide resolved
LOG.debug("New block added event: " + event);
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

Wondering if this is an unnecessary log or too much for DEBUG level...If you think there is value in this log, I would lean towards putting this at TRACE level and/or only logging went something significant occurs, either a state change where we can print some details about what changed; or a (potentially ignorable) error.

If I'm reading it correctly, BlockAddedEvent will only give details about the block event (which is already logged elsewhere) rather than anything useful about the pruning operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, the logging here is unnecessary. Removed.

// Get pruning mark
final long blockNumber = event.getBlock().getHeader().getNumber();
long pruningMark = prunerStorage.getPruningMark().orElse(blockNumber);
if (blockNumber < pruningMark) {
// Ignore and warn if block number < pruning mark, this normally indicates the blocksToRetain
// is too small.
LOG.warn(
"Block added event: "
+ event
+ " has a block number of "
+ blockNumber
+ " < pruning mark "
+ pruningMark);
return;
}
// Append block into fork blocks.
final KeyValueStorageTransaction tx = prunerStorage.startTransaction();
final Collection<Hash> forkBlocks = prunerStorage.getForkBlocks(blockNumber);
forkBlocks.add(event.getBlock().getHash());
prunerStorage.setForkBlocks(tx, blockNumber, forkBlocks);
// If a block is a new canonical head, start pruning.
if (event.isNewCanonicalHead()
&& blockNumber - blocksToRetain - pruningMark >= pruningFrequency) {
while (blockNumber - pruningMark >= blocksToRetain) {
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
LOG.debug("Pruning chain data at pruning mark: " + pruningMark);
wcgcyx marked this conversation as resolved.
Show resolved Hide resolved
pruneChainDataAtBlock(tx, pruningMark);
pruningMark++;
}
}
// Update pruning mark and commit
prunerStorage.setPruningMark(tx, pruningMark);
tx.commit();
}

private void pruneChainDataAtBlock(final KeyValueStorageTransaction tx, final long blockNumber) {
// Get a collection of old fork blocks that need to be pruned.
final Collection<Hash> oldForkBlocks = prunerStorage.getForkBlocks(blockNumber);
final BlockchainStorage.Updater updater = blockchainStorage.updater();
for (Hash toPrune : oldForkBlocks) {
jframe marked this conversation as resolved.
Show resolved Hide resolved
// Prune block header, body, receipts, total difficulty and transaction locations.
updater.removeBlockHeader(toPrune);
updater.removeBlockBody(toPrune);
updater.removeTransactionReceipts(toPrune);
updater.removeTotalDifficulty(toPrune);
blockchainStorage
.getBlockBody(toPrune)
.ifPresent(
blockBody ->
blockBody
.getTransactions()
.forEach(t -> updater.removeTransactionLocation(t.getHash())));
}
// Prune canonical chain mapping and commit.
updater.removeBlockHash(blockNumber);
updater.commit();
// Remove old fork blocks.
prunerStorage.removeForkBlocks(tx, blockNumber);
}
}
Loading