Skip to content

lsm/sst: read bloom filters lazily#31008

Open
andrwng wants to merge 1 commit into
redpanda-data:devfrom
andrwng:lsm-lazy-bloom
Open

lsm/sst: read bloom filters lazily#31008
andrwng wants to merge 1 commit into
redpanda-data:devfrom
andrwng:lsm-lazy-bloom

Conversation

@andrwng

@andrwng andrwng commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The bloom filter scales with the SST: ~1.5% of the file. We loaded the whole filter at open and pinned it for the reader's lifetime, so resident memory grew with (open readers x SST size). That's fine for L0 but doesn't hold as the metastore fills deeper levels, where a single SST's filter runs to hundreds of MiB.

open() now retains the per-region offset array and reads each region from the file on demand. In order to still validate the on-disk contents, open() will read the whole bloom filter and validate the overall CRC, and also keep track of a not-persisted CRC of each filter region. Upon loading regions from disk thereafter, the region CRCs are validated.

A test is added that corrupts a region byte after open and asserts the lazy read is rejected; sst_test covers the open-time whole-block check.

NOTE: open IO is unchanged; the win is resident memory, not fewer reads.

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v26.1.x
  • v25.3.x
  • v25.2.x

Release Notes

  • None

The bloom filter scales with the SST: ~1.5% of the file. We loaded the
whole filter at open and pinned it for the reader's lifetime, so resident
memory grew with (open readers x SST size). That's fine for L0 but
doesn't hold as the metastore fills deeper levels, where a single SST's
filter runs to hundreds of MiB.

open() now retains the per-region offset array and reads each region
from the file on demand. In order to still validate the on-disk
contents, open() will read the whole bloom filter and validate the
overall CRC, and also keep track of a not-persisted CRC of each filter
region.  Upon loading regions from disk thereafter, the region CRCs are
validated.

A test is added that corrupts a region byte after open and asserts the
lazy read is rejected; sst_test covers the open-time whole-block check.

NOTE: open IO is unchanged; the win is resident memory, not fewer reads.
Copilot AI review requested due to automatic review settings July 2, 2026 23:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR changes SST bloom-filter handling to reduce resident memory growth by lazily reading bloom filter regions on demand, while preserving integrity checks via an open-time whole-block CRC and per-region CRC validation on subsequent reads.

Changes:

  • Make lsm::block::filter_reader lazily read filter regions from the backing random_access_file_reader and validate each region against a CRC computed at open.
  • Update lsm::sst::reader to use the new async filter reader API (filter_reader::open / async key_may_match).
  • Add/extend tests to verify corruption is detected both at SST open time (whole-block CRC) and during lazy region reads (per-region CRC).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/v/lsm/sst/tests/sst_test.cc Adds an SST-level corruption test that flips a filter byte and expects open-time CRC validation to fail.
src/v/lsm/sst/tests/BUILD Adds deps needed by the new SST corruption test utilities.
src/v/lsm/sst/reader.cc Switches filter loading to filter_reader::open and awaits async key_may_match.
src/v/lsm/block/tests/filter_test.cc Reworks tests to exercise lazy region reads and validate per-region CRC detection after open.
src/v/lsm/block/tests/BUILD Updates deps for new filter tests (crc32c, ioarray, exceptions).
src/v/lsm/block/filter.h Replaces eager in-memory filter reader with lazy async API and documents integrity approach.
src/v/lsm/block/filter.cc Implements lazy region reads plus open-time whole-block CRC and per-region CRC computation/validation.
src/v/lsm/block/BUILD Adds implementation deps needed for CRC/compression/exceptions in the new filter reader.

Comment on lines +47 to +50
ss::future<ioarray> read(size_t offset, size_t n) override {
n = std::min(n, _data.size_bytes() - offset);
co_return ioarray::copy_from(_data.share(offset, n));
}
Comment on lines +42 to +45
ss::future<ioarray> read(size_t offset, size_t n) override {
n = std::min(n, _data.size_bytes() - offset);
co_return ioarray::copy_from(_data.share(offset, n));
}
Comment on lines +53 to +66
void corrupt_byte(size_t offset) {
std::string buf(_data.size_bytes(), '\0');
auto* p = buf.data();
iobuf::iterator_consumer(_data.cbegin(), _data.cend())
.consume(buf.size(), [&p](const char* src, size_t sz) {
std::memcpy(p, src, sz);
p += sz;
return ss::stop_iteration::no;
});
buf[offset] = static_cast<char>(~static_cast<uint8_t>(buf[offset]));
iobuf out;
out.append(buf.data(), buf.size());
_data = std::move(out);
}
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#86688
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(PASS) ShadowLinkingRandomOpsTest test_node_operations {"failures": false, "workload_set": "cloud_combos"} integration https://buildkite.com/redpanda/redpanda/builds/86688#019f2539-b0b7-4476-9e2c-d9e5a1dc52b0 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0183, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShadowLinkingRandomOpsTest&test_method=test_node_operations
FLAKY(PASS) ShadowLinkingRandomOpsTest test_node_operations {"failures": true, "workload_set": "cloud_combos"} integration https://buildkite.com/redpanda/redpanda/builds/86688#019f2539-b0b8-4607-a5a1-a703e573d9ff 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0183, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShadowLinkingRandomOpsTest&test_method=test_node_operations

@andrwng andrwng changed the title lsm/sst: read bloom filters lazily, with verified regions lsm/sst: read bloom filters lazily Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants