Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.util.InvalidConfigurationException;
import org.hyperledger.besu.util.Subscribers;
Expand All @@ -49,8 +50,11 @@
import java.util.stream.Stream;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import io.prometheus.client.guava.cache.CacheMetricsCollector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -73,6 +77,20 @@ public class DefaultBlockchain implements MutableBlockchain {

private Comparator<BlockHeader> blockChoiceRule;

private static final int BLOCK_HEADERS_CACHE_SIZE = 2048;
private static final int BLOCK_BODIES_CACHE_SIZE = 2048;
private static final int TRX_RECEIPTS_CACHE_SIZE = 2048;
private static final int TOTAL_DIFFICULTY_CACHE_SIZE = 2048;

private final Cache<Hash, BlockHeader> blockHeadersCache =
CacheBuilder.newBuilder().recordStats().maximumSize(BLOCK_HEADERS_CACHE_SIZE).build();
private final Cache<Hash, BlockBody> blockBodiesCache =
CacheBuilder.newBuilder().recordStats().maximumSize(BLOCK_BODIES_CACHE_SIZE).build();
private final Cache<Hash, List<TransactionReceipt>> transactionReceiptsCache =
CacheBuilder.newBuilder().recordStats().maximumSize(TRX_RECEIPTS_CACHE_SIZE).build();
private final Cache<Hash, Difficulty> totalDifficultyCache =
CacheBuilder.newBuilder().recordStats().maximumSize(TOTAL_DIFFICULTY_CACHE_SIZE).build();

private DefaultBlockchain(
final Optional<Block> genesisBlock,
final BlockchainStorage blockchainStorage,
Expand Down Expand Up @@ -144,6 +162,14 @@ private DefaultBlockchain(

this.reorgLoggingThreshold = reorgLoggingThreshold;
this.blockChoiceRule = heaviestChainBlockChoiceRule;

CacheMetricsCollector cacheMetrics = new CacheMetricsCollector();
cacheMetrics.addCache("blockHeaders", blockHeadersCache);
cacheMetrics.addCache("blockBodies", blockBodiesCache);
cacheMetrics.addCache("transactionReceipts", transactionReceiptsCache);
cacheMetrics.addCache("totalDifficulty", totalDifficultyCache);
if (metricsSystem instanceof PrometheusMetricsSystem prometheusMetricsSystem)
prometheusMetricsSystem.addCollector(BesuMetricCategory.BLOCKCHAIN, () -> cacheMetrics);
}

public static MutableBlockchain createMutable(
Expand Down Expand Up @@ -232,17 +258,20 @@ public Optional<BlockHeader> getBlockHeader(final long blockNumber) {

@Override
public Optional<BlockHeader> getBlockHeader(final Hash blockHeaderHash) {
return blockchainStorage.getBlockHeader(blockHeaderHash);
return Optional.ofNullable(blockHeadersCache.getIfPresent(blockHeaderHash))
.or(() -> blockchainStorage.getBlockHeader(blockHeaderHash));
}

@Override
public Optional<BlockBody> getBlockBody(final Hash blockHeaderHash) {
return blockchainStorage.getBlockBody(blockHeaderHash);
return Optional.ofNullable(blockBodiesCache.getIfPresent(blockHeaderHash))
.or(() -> blockchainStorage.getBlockBody(blockHeaderHash));
}

@Override
public Optional<List<TransactionReceipt>> getTxReceipts(final Hash blockHeaderHash) {
return blockchainStorage.getTransactionReceipts(blockHeaderHash);
return Optional.ofNullable(transactionReceiptsCache.getIfPresent(blockHeaderHash))
.or(() -> blockchainStorage.getTransactionReceipts(blockHeaderHash));
}

@Override
Expand All @@ -252,7 +281,8 @@ public Optional<Hash> getBlockHashByNumber(final long number) {

@Override
public Optional<Difficulty> getTotalDifficultyByHash(final Hash blockHeaderHash) {
return blockchainStorage.getTotalDifficulty(blockHeaderHash);
return Optional.ofNullable(totalDifficultyCache.getIfPresent(blockHeaderHash))
.or(() -> blockchainStorage.getTotalDifficulty(blockHeaderHash));
}

@Override
Expand Down Expand Up @@ -283,14 +313,23 @@ public void setBlockChoiceRule(final Comparator<BlockHeader> blockChoiceRule) {

@Override
public synchronized void appendBlock(final Block block, final List<TransactionReceipt> receipts) {
cacheBlockData(block, receipts);
appendBlockHelper(new BlockWithReceipts(block, receipts), false);
}

@Override
public synchronized void storeBlock(final Block block, final List<TransactionReceipt> receipts) {
cacheBlockData(block, receipts);
appendBlockHelper(new BlockWithReceipts(block, receipts), true);
}

private void cacheBlockData(final Block block, final List<TransactionReceipt> receipts) {
blockHeadersCache.put(block.getHash(), block.getHeader());
blockBodiesCache.put(block.getHash(), block.getBody());
transactionReceiptsCache.put(block.getHash(), receipts);
totalDifficultyCache.put(block.getHash(), block.getHeader().getDifficulty());
}

private boolean blockShouldBeProcessed(
final Block block, final List<TransactionReceipt> receipts) {
checkArgument(
Expand Down