Skip to content

perf(fullmap): zlib-rs decompression + redb cursor bulk inserts - #73

Merged
SkyeAv merged 2 commits into
mainfrom
feat/fullmap-redb-cursor-zlibrs
Aug 10, 2026
Merged

perf(fullmap): zlib-rs decompression + redb cursor bulk inserts#73
SkyeAv merged 2 commits into
mainfrom
feat/fullmap-redb-cursor-zlibrs

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Three fullmap build/storage-stack improvements, each measured on this repo's build path. No behavior change, no schema bump, no forced rebuild — the file format stays redb v3 (bidirectional cross-version reads verified).

1. flate2 → zlib-rs decompression backend

The build's producer threads decompress ~30–40 GB of BABEL .gz. Switched flate2 from the default miniz_oxide backend to zlib-rs (pure Rust, runtime SIMD multiversioning — no C toolchain needed for wheels).

Measured on a real 46 MB BABEL synonym stream (synonyms/GeneProteinConflated.txt.gz):

backend decompressed throughput time/iter (46 MB)
miniz_oxide (old) ~3.0 GB/s 0.190 s
zlib-rs (new) ~4.6 GB/s 0.127 s

~1.5× faster decompression.

2. redb engine: 4.1 → 4.2-to-be (pinned master)

redb = "4" (crates.io 4.1.0) → git-pinned cberner/redb master rev a35e7cc for the ascending-key insert optimization (not yet published; latest stable is still 4.1.0).

Measured effect: the 16 RECORDS shard files shrink ~50% on identical input:

shard files total
redb 4.1 539.0 MB
redb master 269.5 MB

This also speeds cold lookups (half the bytes to page in) and halves the DB's disk footprint. The file format is unchanged (still redb v3) — a drop-in engine swap: existing v5 DBs keep opening, no rebuild required. Cross-version read compatibility verified both directions.

Swap back to redb = "4.2" once 4.2.0 publishes on crates.io.

3. Cursor bulk inserts

flush_shard_batch now appends through redb's experimental cursor API (upper_bound_mut(Unbounded) + insert_before) instead of per-key table.insert().

Measured (internally-controlled microbenchmark: 2 M ascending fixed-size records, both paths in one process, 2 rounds):

time (2 M inserts) throughput
plain insert() 0.89 s 2.0 M/s
cursor insert_before() 0.22 s 8.1 M/s

~4× faster insert throughput at identical file size.

The cursor requires strictly ascending keys and never overwrites (UnorderedKey), so the rare duplicate xxh64 hash (two distinct terms colliding; ~6% odds per full build) falls back to a plain insert() for that one record — preserving the historical overwrite semantics — and reopens the cursor. last is tracked across batch flushes (owned by write_shard_records) so a collision straddling a batch boundary is caught too. Covered by a dedicated regression test (intra-batch + cross-batch).

Parser benchmark (no change)

The plan asked to test whether swapping serde_json for sonic-rs or simd-json helps. Benchmarked now on real BABEL rows — decided no:

dataset serde_json sonic-rs simd-json
GeneFamily (28 k rows, 447 B avg) 1.94 M/s 1.56 M/s (−20%) 1.38 M/s (−29%)
GeneProteinConflated (100 k rows, 543 B avg) 1.31 M/s 1.37 M/s (+5%) 1.14 M/s (−13%)

sonic-rs's best case is ~5% on larger rows — noise-level, not worth a dependency swap. The current borrowed-serde design is already the fastest choice. No change.

make check

Green: ruff, ruff-format, cargo-fmt, pyright, pytest (full suite), cargo test (68 + 10), cargo clippy -D warnings.

Review

The CODE_REVIEWER subagent returned empty; I reviewed the diff myself and found a critical bug: the cursor collision fallback only caught duplicates within a single batch (last was local to each flush_shard_batch call), so a duplicate hash straddling a batch boundary would raise UnorderedKey → build failure. Fixed by threading last across batches; extended the regression test to cover the cross-batch case (7b7f9af).

Summary by CodeRabbit

  • Performance

    • Improved fullmap shard writing for faster data generation.
    • Reduced shard file sizes by approximately 50% without changing the existing schema.
    • Improved compressed data decompression performance.
  • Compatibility

    • Existing databases remain compatible.
    • Duplicate records are handled safely while preserving expected overwrite behavior.
  • Documentation

    • Updated fullmap documentation with current shard-writing behavior and performance improvements.

SkyeAv added 2 commits August 10, 2026 16:15
Advance the fullmap build's storage stack with three measured wins:

- flate2 backend switched from miniz_oxide to zlib-rs (pure Rust, fastest
  api-compatible DEFLATE decompressor). The build's producer threads
  decompress ~30-40 GB of BABEL .gz; on a real 46 MB BABEL synonym stream
  zlib-rs decompressed at ~4.6 GB/s vs miniz_oxide's ~3.0 GB/s (~1.5x).

- redb pinned to cberner/redb master (the 4.2-to-be, rev a35e7cc) for the
  automatic ascending-key insert optimization. Measured effect: the 16
  RECORDS shard files shrink ~50% on identical input (a 539 MB shard set
  rebuilds to 270 MB), which also speeds cold lookups. File format is
  unchanged (still redb v3) — a drop-in engine swap with no forced rebuild
  and bidirectional cross-version read compatibility (verified).

- Phase-4 RECORDS write now appends through redb's experimental cursor API
  (upper_bound_mut + insert_before). An internally-controlled microbenchmark
  (2M ascending records, both paths in one process) measured the cursor at
  ~4x the insert throughput of plain insert() (0.22s vs 0.89s) at identical
  file size. The cursor requires strictly ascending keys and never
  overwrites, so a duplicate xxh64 hash (two distinct terms colliding) falls
  back to a plain insert() for that one record — preserving the historical
  overwrite semantics — and reopens the cursor; covered by a dedicated test.

Swap back to redb = "4.2" once 4.2.0 publishes on crates.io.

make check green: ruff, ruff-format, cargo-fmt, pyright, pytest (full suite),
cargo test (68 + 10), cargo clippy -D warnings.
…on fallback

The cursor collision fallback (close → plain insert → reopen) only caught
duplicates WITHIN a single batch because `last` was local to each
`flush_shard_batch` call. A duplicate xxh64 hash straddling a BATCH BOUNDARY
(group N in batch K, group N+1 — same hash, different term — in batch K+1)
would hit `insert_before` with a non-ascending key → `UnorderedKey` → build
failure. The old `insert()` never had this problem.

Move `last: Option<u64>` to `write_shard_records` (owned across all flushes)
and thread `&mut last` into `flush_shard_batch`. Extended the regression test
to cover both intra-batch and cross-batch collisions.

make check green.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Rust crate now uses pinned redb cursor support and zlib-rs. Shard RECORDS writes use cursor-based batch insertion with fallback handling for duplicate hashes. Documentation, changelog entries, and regression tests reflect these changes.

Changes

Shard write optimization

Layer / File(s) Summary
Backend configuration
rust/Cargo.toml
Runtime and development dependencies use a pinned redb revision with experimental_cursor. flate2 uses the zlib-rs backend.
Cursor-based shard writes and validation
rust/src/fullmap.rs, docs/fullmap.md, CHANGELOG.md
Shard batches use ascending cursor insertion. Duplicate or non-increasing hashes use regular insertion and reopen the cursor. Tests cover duplicates within and across batches, including later-value-wins behavior. Documentation and changelog entries describe the changes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • SkyeAv/Tablassert#63: Both changes modify rust/src/fullmap.rs and fullmap shard-writing behavior, but address different concerns.

Sequence Diagram(s)

sequenceDiagram
  participant write_shard_records
  participant flush_shard_batch
  participant redb_RECORDS
  write_shard_records->>flush_shard_batch: flush sorted batch and last hash
  flush_shard_batch->>redb_RECORDS: insert ascending hashes through cursor
  flush_shard_batch->>redb_RECORDS: regular insert for duplicate or non-increasing hash
  redb_RECORDS-->>flush_shard_batch: reopen cursor and retain last hash
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the two primary performance changes: zlib-rs decompression and redb cursor bulk inserts.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fullmap-redb-cursor-zlibrs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/src/fullmap.rs`:
- Line 2067: Replace the unstable ordering in the batch-processing flow with a
stable sort so records sharing the same hash retain their original insertion
order. Update the sort around batch.sort_unstable_by_key, preserving hash
ordering while ensuring Table::insert processes later equal-hash records after
earlier ones.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5806f1de-be7e-4bea-8fcc-b44bec430afe

📥 Commits

Reviewing files that changed from the base of the PR and between 6a54349 and 7b7f9af.

⛔ Files ignored due to path filters (1)
  • rust/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • CHANGELOG.md
  • docs/fullmap.md
  • rust/Cargo.toml
  • rust/src/fullmap.rs

Comment thread rust/src/fullmap.rs
if batch.is_empty() {
return Ok(0);
}
batch.sort_unstable_by_key(|(hash, _)| *hash);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve equal-hash insertion order.

sort_unstable_by_key can reorder records that have the same hash. The fallback then applies Table::insert in that unspecified order. This can store the earlier term instead of the later term for an intra-batch xxh64 collision.

Use a stable sort, or add the original sequence as a tie-breaker, so the documented later-value-wins behavior remains deterministic.

Proposed fix
-    batch.sort_unstable_by_key(|(hash, _)| *hash);
+    batch.sort_by_key(|(hash, _)| *hash);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
batch.sort_unstable_by_key(|(hash, _)| *hash);
batch.sort_by_key(|(hash, _)| *hash);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/src/fullmap.rs` at line 2067, Replace the unstable ordering in the
batch-processing flow with a stable sort so records sharing the same hash retain
their original insertion order. Update the sort around
batch.sort_unstable_by_key, preserving hash ordering while ensuring
Table::insert processes later equal-hash records after earlier ones.

@SkyeAv
SkyeAv merged commit 89f276b into main Aug 10, 2026
5 checks passed
@SkyeAv
SkyeAv deleted the feat/fullmap-redb-cursor-zlibrs branch August 10, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant