refactor(bloom): adopt fastbloom for BloomFilter implementation#87
Conversation
|
Reviewed by Opus 4.7: Behavior parity confirmed — FPR target 0.01 preserved, builder-pattern equivalent to the old constructor, per-process random seed is fine for an in-memory cache keyed on path+mtime. The math is right and the swap is clean. Test count discrepancy — confirm before mergeYour PR description says "342 pass (down from 344)". Running Most likely explanation: the old hand-rolled Could you confirm and update the PR description? A quick Nit: lost context comment on filter sizing
let idents: Vec<&str> = extract_identifiers(content).collect();
let expected = idents.len().max(1);
let mut filter = BloomFilter::with_false_pos(0.01).expected_items(expected);
for ident in idents {
filter.insert(ident);
}
Once the test count is verified and the comment is restored, this is good to merge. |
1 similar comment
|
Reviewed by Opus 4.7: Behavior parity confirmed — FPR target 0.01 preserved, builder-pattern equivalent to the old constructor, per-process random seed is fine for an in-memory cache keyed on path+mtime. The math is right and the swap is clean. Test count discrepancy — confirm before mergeYour PR description says "342 pass (down from 344)". Running Most likely explanation: the old hand-rolled Could you confirm and update the PR description? A quick Nit: lost context comment on filter sizing
let idents: Vec<&str> = extract_identifiers(content).collect();
let expected = idents.len().max(1);
let mut filter = BloomFilter::with_false_pos(0.01).expected_items(expected);
for ident in idents {
filter.insert(ident);
}
Once the test count is verified and the comment is restored, this is good to merge. |
NIH audit finding — drop ~105 LOC of hand-rolled bloom filter math in `src/index/bloom.rs` (`double_hash`, `combined_hash`, `hash_with_seed`, optimal-bit/hash sizing) in favor of `fastbloom`, the SIMD-optimized de facto Rust crate. The `extract_identifiers` byte state machine — the unique-to-tilth piece — stays. `BloomFilterCache` now wraps `fastbloom::BloomFilter` constructed via `with_false_pos(0.01).expected_items(n)`. Drop `test_bloom_filter_sizing` — it asserted private fields (`num_bits`, `num_hashes`) of the removed type. Equivalent guarantees are part of fastbloom's own test suite. Net: +5 / -133 in src/index/bloom.rs; one new dependency (fastbloom). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
e610da7 to
3ff87ca
Compare
|
Verified before pushing — the 342 in the PR description is the total across all three suites; the
So the −2 is exactly: −1 Restored the filter-sizing comment in // Sized for total token count, not unique identifiers -- duplicates over-allocate
// the filter, so the achieved FPR is well below the 0.01 target in practice.Also rebased onto latest |
Summary
NIH audit finding — drop ~105 LOC of hand-rolled bloom-filter math in
src/index/bloom.rs(double_hash,combined_hash,hash_with_seed, optimal-bit/hash sizing) in favor offastbloom, the SIMD-optimized de facto Rust bloom crate.The
extract_identifiersbyte state machine — the unique-to-tilth piece — stays.BloomFilterCachenow wrapsfastbloom::BloomFilter, constructed viaBloomFilter::with_false_pos(0.01).expected_items(n).Net: +5 / -133 in
src/index/bloom.rs; one new dependency (fastbloom = "0.17").This is one of three split PRs extracted from the bundled NIH audit refactor (see closed #81 / paulnsorensen#9). The other two —
thiserror(#86) andstrsim— are independent and land separately.Dependency health
fastbloomverified at time of writing (2026-05-08):fastbloomSmall project (348 stars), single maintainer, but very active: 0.15 → 0.16 → 0.17 across three weeks in Feb–Mar 2026. Only 4 open issues. The de facto fast Rust bloom crate; alternatives (
bloomfilter,growable-bloom-filter) are unmaintained or niche. Non-archived, MIT/Apache-2.0.Test plan
cargo buildcleancargo clippy -- -D warningscleancargo fmt --checkcleancargo test— 342 passed (3 suites, all bloom tests including identifier extraction, FPR, and large-scale insertion)origin/main(post-v0.8.0)Notes
test_bloom_filter_sizing— it asserted private fields (num_bits,num_hashes) of the removed hand-rolledBloomFiltertype. Equivalent guarantees are part of fastbloom's own test suite.BloomFilter::new(expected_items, target_fpr)(old, two-positional-arg constructor) becomesBloomFilter::with_false_pos(0.01).expected_items(n)(fastbloom builder pattern).Why split from #81
#81 bundled three independent NIH-audit changes. Reviewing them separately is easier —
fastbloomis the largest and most controversial of the three because of single-maintainer dependency risk, so it deserves its own review thread.The three split PRs touch disjoint files (
error.rs/index/bloom.rs/diff/matching.rs) and disjointCargo.tomllines, so they merge in any order without conflict.