Repository navigation
Partition index and filter blocks of the entry location RocksDB - #4909
Merged
merlimat merged 2 commits intoOct 15, 2026
Merged
Conversation
The entry location index caches index and filter blocks in the block cache but keeps one full Bloom filter block per SST. For a 64 MiB SST that block is ~8.7 MiB, larger than a shard of the LRU block cache (capacity / 64; 3.2 MiB with the default 2 GiB of direct memory). The LRU cache still inserts it, evicting the rest of the shard, and frees it as soon as the reader releases it, so every point lookup reads, checksums and parses the whole filter again. Use kTwoLevelIndexSearch with partitioned filters and pin only the top-level index and filter blocks, as RocksDB recommends when index and filter blocks live in the block cache. Partitions are ~4 KiB. The layout is readable by RocksDB 7.9.2, and SSTs stay at format_version 5. The new dbStorage_rocksDB_partitionedIndexAndFiltersEnabled setting (default true) disables it; the shipped entry_location_rocksdb.conf template sets the same options.
lhotari
approved these changes
Oct 15, 2026
lhotari
left a comment
Member
There was a problem hiding this comment.
LGTM. Thanks for the careful analysis and benchmark behind this fix for the oversized filter blocks. The option combination (two-level index, partitioned filters, pinned top-level blocks) is valid for RocksDB 10.10.1 and stays on format_version 5, and the on-disk layout is readable by older releases. The only note is a non-blocking test suggestion inline.
…yout test Write 10 ledgers of 1000 entries instead of a single key, so the filter spans several 4 KiB partitions and, with 256-byte data blocks, the index spans several partitions too. Read back every entry, absent entries and the last entry of each ledger, with and without partitioning.
codelipenghui
approved these changes
Oct 15, 2026
merlimat
added a commit
that referenced
this pull request
Oct 17, 2026
* Partition index and filter blocks of the entry location RocksDB The entry location index caches index and filter blocks in the block cache but keeps one full Bloom filter block per SST. For a 64 MiB SST that block is ~8.7 MiB, larger than a shard of the LRU block cache (capacity / 64; 3.2 MiB with the default 2 GiB of direct memory). The LRU cache still inserts it, evicting the rest of the shard, and frees it as soon as the reader releases it, so every point lookup reads, checksums and parses the whole filter again. Use kTwoLevelIndexSearch with partitioned filters and pin only the top-level index and filter blocks, as RocksDB recommends when index and filter blocks live in the block cache. Partitions are ~4 KiB. The layout is readable by RocksDB 7.9.2, and SSTs stay at format_version 5. The new dbStorage_rocksDB_partitionedIndexAndFiltersEnabled setting (default true) disables it; the shipped entry_location_rocksdb.conf template sets the same options. * Exercise lookups across several index and filter partitions in the layout test Write 10 ledgers of 1000 entries instead of a single key, so the filter spans several 4 KiB partitions and, with 256-byte data blocks, the index spans several partitions too. Read back every entry, absent entries and the last entry of each ledger, with and without partitioning.
merlimat
added a commit
that referenced
this pull request
Oct 17, 2026
* Partition index and filter blocks of the entry location RocksDB The entry location index caches index and filter blocks in the block cache but keeps one full Bloom filter block per SST. For a 64 MiB SST that block is ~8.7 MiB, larger than a shard of the LRU block cache (capacity / 64; 3.2 MiB with the default 2 GiB of direct memory). The LRU cache still inserts it, evicting the rest of the shard, and frees it as soon as the reader releases it, so every point lookup reads, checksums and parses the whole filter again. Use kTwoLevelIndexSearch with partitioned filters and pin only the top-level index and filter blocks, as RocksDB recommends when index and filter blocks live in the block cache. Partitions are ~4 KiB. The layout is readable by RocksDB 7.9.2, and SSTs stay at format_version 5. The new dbStorage_rocksDB_partitionedIndexAndFiltersEnabled setting (default true) disables it; the shipped entry_location_rocksdb.conf template sets the same options. * Exercise lookups across several index and filter partitions in the layout test Write 10 ledgers of 1000 entries instead of a single key, so the filter spans several 4 KiB partitions and, with 256-byte data blocks, the index spans several partitions too. Read back every entry, absent entries and the last entry of each ledger, with and without partitioning.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
DbLedgerStorage's entry location index (KeyValueStorageRocksDB,DbConfigType.EntryLocation) keeps index andfilter blocks in the block cache (
cache_index_and_filter_blocks=true). It uses the default index type and onefull Bloom filter (10 bits/key) per SST, with a 64 MiB target file size. A full filter covers the whole file: about
8.7 MiB for a 64 MiB SST of
(ledgerId, entryId)keys.The block cache is
new LRUCache(size). RocksDB splits any cache of 32 MiB or more into 64 shards, and a blocklives in one shard. The default size is
maxDirectMemory / ledgerDirs / 10, so with the default 2 GiB of directmemory and one ledger directory it is 205 MiB and each shard holds 3.2 MiB. An 8.7 MiB filter block needs a shard
of at least 8.7 MiB, which means a cache of about 560 MiB per ledger directory.
In RocksDB 10.10.1's
LRUCacheShard, inserting a block bigger than its shard first evicts every unreferencedentry in the shard, then inserts the block anyway.
Release()frees it immediately because the shard is overcapacity. The filter is not pinned, so every
Get()on every overlapping SST:With buffered I/O, BookKeeper's default, these reads come from the page cache and lookups become CPU-bound.
With direct I/O, or when the page cache is tight, they go to disk.
EntryLocationIndex.getLocation()runs onevery read-cache miss of a bookie, so this limits bookie read throughput.
Example
Current code (
KeyValueStorageRocksDB.initializeRocksDBWithBookieConf):The benchmark drives
EntryLocationIndexthrough the default bookie-conf path:getLocation()from 4 threads: 5 s warm-up, then 20 s measured.Floor lookups ran on the same DB, right after the get phase in the same process:
getLastEntryInLedger()(
getFloor) on a random ledger, 4 threads, 5 s warm-up, then 20 s measured.Change
This is the setup RocksDB recommends when index and filter blocks live in the block cache
(https://github.com/facebook/rocksdb/wiki/Partitioned-Index-Filters):
metadata_block_size).Other changes:
dbStorage_rocksDB_partitionedIndexAndFiltersEnableddefaults totrueand turns this off. It is documented inbk_server.confandreference/config.md.conf/entry_location_rocksdb.conf.defaultand its test copy setindex_type=kTwoLevelIndexSearch,partition_filters=trueandpin_top_level_index_and_filter=true.Compatibility:
format_version=5.decouple_partitioned_filters=trueis readable by every version that reads partitioned filters./api/v1/bookie/entry_location_compactorentryLocationCompactionInterval.Trade-off: the second index level costs an extra lookup. When the whole index fits in the block cache with room
to spare (the 1 GiB row), point gets are about 9% slower. Floor lookups are about 5% slower whenever the blocks
they touch are cached (the 256 MiB and 1 GiB rows). Real location indexes are usually much larger than the block
cache.
Testing
KeyValueStorageRocksDBTest#testEntryLocationPartitionedIndexAndFilters:SstFileReader(index partitions) and the filter type from the SST's metaindex key (partitionedfilter.vsfullfilter.).org.apache.bookkeeper.bookie.storage.ldbtests pass, exceptDbLedgerStorageWithDirectEntryLoggerTest. It fails locally because there is no native-io JNI library for macOS/aarch64; this change doesn't touch it.