Skip to content

Commit 3ff1592

Browse files
committed
feat: add --cache-index-filter-blocks flag
By default, index and filter blocks live on the heap where they may never be evicted (~18 MB per SST file). Pass --cache-index-filter-blocks to store them in the block cache instead, bounding memory at the cost of possible eviction under cache pressure.
1 parent 74e4b61 commit 3ff1592

3 files changed

Lines changed: 25 additions & 14 deletions

File tree

src/config.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ pub struct Config {
7070
/// Must stay within db_write_buffer_size_mb to avoid mid-batch flushes.
7171
pub initial_sync_batch_size: usize,
7272

73+
/// Store index and filter blocks inside the block cache (default: false).
74+
/// When enabled, bounds memory but allows eviction under pressure.
75+
/// When disabled (default), index/filter blocks stay on the heap and are
76+
/// may never be evicted, giving better read performance at the cost of ~18 MB
77+
/// per SST file of unbounded memory.
78+
pub db_cache_index_filter_blocks: bool,
79+
7380
#[cfg(feature = "liquid")]
7481
pub parent_network: BNetwork,
7582
#[cfg(feature = "liquid")]
@@ -256,6 +263,10 @@ impl Config {
256263
.help("Number of blocks per batch during initial sync. Larger values keep more txo rows in the write buffer during indexing, improving lookup_txos cache hit rate for recently-created outputs.")
257264
.takes_value(true)
258265
.default_value("250")
266+
).arg(
267+
Arg::with_name("cache_index_filter_blocks")
268+
.long("cache-index-filter-blocks")
269+
.help("Store index/filter blocks in the block cache instead of on the heap. Bounds memory but allows eviction under cache pressure.")
259270
).arg(
260271
Arg::with_name("zmq_addr")
261272
.long("zmq-addr")
@@ -496,6 +507,7 @@ impl Config {
496507
db_parallelism: value_t_or_exit!(m, "db_parallelism", usize),
497508
db_write_buffer_size_mb: value_t_or_exit!(m, "db_write_buffer_size_mb", usize),
498509
initial_sync_batch_size: value_t_or_exit!(m, "initial_sync_batch_size", usize),
510+
db_cache_index_filter_blocks: m.is_present("cache_index_filter_blocks"),
499511
zmq_addr,
500512

501513
#[cfg(feature = "liquid")]

src/new_index/db.rs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,18 @@ impl DB {
150150
let mut block_opts = rocksdb::BlockBasedOptions::default();
151151
let cache_size_bytes = config.db_block_cache_mb * 1024 * 1024;
152152
block_opts.set_block_cache(&rocksdb::Cache::new_lru_cache(cache_size_bytes));
153-
// Store index and filter blocks inside the block cache so their memory is
154-
// bounded by --db-block-cache-mb. Without this, RocksDB allocates table-reader
155-
// memory (index + filter blocks) on the heap separately for every open SST file.
156-
// During initial sync, L0 files accumulate up to the compaction trigger (64 by
157-
// default) and this unbounded heap allocation can grow to many GB.
158-
// Note: increase --db-block-cache-mb proportionally (e.g. 4096) so the cache is
159-
// large enough to hold the working set of filter/index blocks without thrashing.
160-
block_opts.set_cache_index_and_filter_blocks(true);
161-
// Pin L0 index and filter blocks in the cache so they are never evicted.
162-
// Without this, data block churn evicts L0 index/filter blocks, causing
163-
// repeated disk reads for every SST lookup — worse than the old heap approach.
164-
// With this, L0 index/filter blocks behave like the old table-reader heap
165-
// allocation but stay within the bounded block cache.
166-
block_opts.set_pin_l0_filter_and_index_blocks_in_cache(true);
153+
// When --cache-index-filter-blocks is passed, store index and filter blocks
154+
// inside the block cache so their memory is bounded by --db-block-cache-mb.
155+
// Without this (the default), RocksDB keeps them on the heap where they may
156+
// never be evicted — possibly better for read performance compared to needing
157+
// to go to disk, but uses ~18 MB per SST file.
158+
if config.db_cache_index_filter_blocks {
159+
block_opts.set_cache_index_and_filter_blocks(true);
160+
// Pin L0 index and filter blocks in the cache so they are never evicted.
161+
// Without this, data block churn evicts L0 index/filter blocks, causing
162+
// repeated disk reads for every SST lookup.
163+
block_opts.set_pin_l0_filter_and_index_blocks_in_cache(true);
164+
}
167165
// Bloom filters allow multi_get() to skip SST files that don't contain a key
168166
// without touching the index or data blocks. Without this, every point lookup
169167
// must binary-search the index of every L0 file whose key range overlaps the

tests/common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ impl TestRunner {
121121
db_parallelism: 2,
122122
db_write_buffer_size_mb: 256,
123123
initial_sync_batch_size: 250,
124+
db_cache_index_filter_blocks: false,
124125
//#[cfg(feature = "electrum-discovery")]
125126
//electrum_public_hosts: Option<crate::electrum::ServerHosts>,
126127
//#[cfg(feature = "electrum-discovery")]

0 commit comments

Comments
 (0)