Skip to content

Commit 7d6b3a2

Browse files
committed
Cache index and filter blocks for archive column families
Archive CFs (ACCOUNT_INFO_STATE_ARCHIVE, ACCOUNT_STORAGE_ARCHIVE, ACCOUNT_INFO_STATE_FREEZER, ACCOUNT_STORAGE_FREEZER) accumulate large numbers of SST files during migration. With cache_index_and_filter_blocks=false (the prior hardcoded default), each SST file's index and filter blocks are held in unbounded native memory outside the block cache, causing RSS to grow ~1.5 GB/hr and reach 15+ GB above control nodes by the time the full 24M-block migration completes. Add isCacheIndexAndFilterBlocks() to SegmentIdentifier (default false, preserving existing behaviour for all other segments) and set it true on the four archive CFs so their index/filter blocks are evictable via the bounded block cache. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Jason Frame <jason.frame@consensys.net>
1 parent e3dedd1 commit 7d6b3a2

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/storage/keyvalue/KeyValueSegmentIdentifier.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,28 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
4444
EnumSet.of(X_BONSAI_ARCHIVE),
4545
true,
4646
false,
47+
true,
4748
true),
4849
ACCOUNT_STORAGE_ARCHIVE(
4950
"ACCOUNT_STORAGE_ARCHIVE".getBytes(StandardCharsets.UTF_8),
5051
EnumSet.of(X_BONSAI_ARCHIVE),
5152
true,
5253
false,
54+
true,
5355
true),
5456
ACCOUNT_INFO_STATE_FREEZER(
5557
"ACCOUNT_INFO_STATE_FREEZER".getBytes(StandardCharsets.UTF_8),
5658
EnumSet.of(X_BONSAI_ARCHIVE),
5759
true,
5860
false,
61+
true,
5962
true),
6063
ACCOUNT_STORAGE_FREEZER(
6164
"ACCOUNT_STORAGE_FREEZER".getBytes(StandardCharsets.UTF_8),
6265
EnumSet.of(X_BONSAI_ARCHIVE),
6366
true,
6467
false,
68+
true,
6569
true),
6670
VARIABLES(new byte[] {11}), // formerly GOQUORUM_PRIVATE_WORLD_STATE
6771

@@ -81,6 +85,7 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
8185
private final boolean containsStaticData;
8286
private final boolean eligibleToHighSpecFlag;
8387
private final boolean staticDataGarbageCollectionEnabled;
88+
private final boolean cacheIndexAndFilterBlocks;
8489

8590
KeyValueSegmentIdentifier(final byte[] id) {
8691
this(id, EnumSet.allOf(DataStorageFormat.class));
@@ -96,11 +101,22 @@ public enum KeyValueSegmentIdentifier implements SegmentIdentifier {
96101
final boolean containsStaticData,
97102
final boolean eligibleToHighSpecFlag,
98103
final boolean staticDataGarbageCollectionEnabled) {
104+
this(id, formats, containsStaticData, eligibleToHighSpecFlag, staticDataGarbageCollectionEnabled, false);
105+
}
106+
107+
KeyValueSegmentIdentifier(
108+
final byte[] id,
109+
final EnumSet<DataStorageFormat> formats,
110+
final boolean containsStaticData,
111+
final boolean eligibleToHighSpecFlag,
112+
final boolean staticDataGarbageCollectionEnabled,
113+
final boolean cacheIndexAndFilterBlocks) {
99114
this.id = id;
100115
this.formats = formats;
101116
this.containsStaticData = containsStaticData;
102117
this.eligibleToHighSpecFlag = eligibleToHighSpecFlag;
103118
this.staticDataGarbageCollectionEnabled = staticDataGarbageCollectionEnabled;
119+
this.cacheIndexAndFilterBlocks = cacheIndexAndFilterBlocks;
104120
}
105121

106122
@Override
@@ -128,6 +144,11 @@ public boolean isStaticDataGarbageCollectionEnabled() {
128144
return staticDataGarbageCollectionEnabled;
129145
}
130146

147+
@Override
148+
public boolean isCacheIndexAndFilterBlocks() {
149+
return cacheIndexAndFilterBlocks;
150+
}
151+
131152
@Override
132153
public boolean includeInDatabaseFormat(final DataStorageFormat format) {
133154
return formats.contains(format);

plugin-api/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Calculated : ${currentHash}
7171
tasks.register('checkAPIChanges', FileStateChecker) {
7272
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
7373
files = sourceSets.main.allJava.files
74-
knownHash = 'fDkUshUOXLQDBHsugUujwwJkJtYVYnqipuScb8CIof0='
74+
knownHash = 'cUSVd6mW2LySwDlG8U7C1tBmFsN7dugAqqRCcyi4BJo='
7575
}
7676
check.dependsOn('checkAPIChanges')
7777

plugin-api/src/main/java/org/hyperledger/besu/plugin/services/storage/SegmentIdentifier.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,16 @@ default boolean includeInDatabaseFormat(final DataStorageFormat format) {
7474
default boolean isStaticDataGarbageCollectionEnabled() {
7575
return false;
7676
}
77+
78+
/**
79+
* Whether index and filter blocks for this segment should be stored in the block cache. When
80+
* false (the default), index and filter blocks are held in unbounded native memory per SST file.
81+
* Segments that accumulate large numbers of SST files (e.g. archive column families written
82+
* during migration) should return true to bound their memory via the block cache.
83+
*
84+
* @return true if index and filter blocks should be cached in the block cache
85+
*/
86+
default boolean isCacheIndexAndFilterBlocks() {
87+
return false;
88+
}
7789
}

plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ private BlockBasedTableConfig createBlockBasedTableConfig(
292292
.setBlockCache(cache)
293293
.setFilterPolicy(new BloomFilter(10, false))
294294
.setPartitionFilters(true)
295-
.setCacheIndexAndFilterBlocks(false)
295+
.setCacheIndexAndFilterBlocks(segment.isCacheIndexAndFilterBlocks())
296296
.setBlockSize(ROCKSDB_BLOCK_SIZE);
297297
}
298298

0 commit comments

Comments
 (0)