Skip to content

refactor(fs): thread Fs through table::Writer and BlobFile creation#107

Merged
polaz merged 16 commits into
mainfrom
feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil
Mar 23, 2026
Merged

refactor(fs): thread Fs through table::Writer and BlobFile creation#107
polaz merged 16 commits into
mainfrom
feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil

Conversation

@polaz
Copy link
Copy Markdown
Member

@polaz polaz commented Mar 22, 2026

Summary

  • Generalize BlockIndexWriter/FilterWriter traits to use generic W instead of hardcoded std::fs::File in finish() methods
  • Make table::Writer, table::MultiWriter, vlog::blob_file::Writer, vlog::blob_file::MultiWriter use Arc<dyn Fs> / Box<dyn FsFile> for pluggable filesystem backends
  • Thread Fs through rewrite_atomic(), fsync_directory(), persist_version(), and upgrade_version()
  • Replace std::fs::create_dir_all / Path::try_exists with Fs::create_dir_all / Fs::exists in tree creation and recovery
  • Update all call sites (flush, compaction, ingestion, recovery) to pass config.fs through

This eliminates the last direct std::fs dependency from the write path, enabling:

  • io_uring: batch SQE submissions for sequential writes during compaction
  • Per-level Fs: new tables written to the appropriate device for their target level

Test plan

  • cargo test --lib --all-features — 519 passed, 0 failed
  • Clean build with zero warnings

Closes #91

Copilot AI review requested due to automatic review settings March 22, 2026 19:25
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 22, 2026

Warning

Rate limit exceeded

@polaz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cc681f65-a401-457f-9881-93f806430ed8

📥 Commits

Reviewing files that changed from the base of the PR and between e99ede9 and 34e94ca.

📒 Files selected for processing (25)
  • src/blob_tree/ingest.rs
  • src/blob_tree/mod.rs
  • src/compaction/filter.rs
  • src/compaction/flavour.rs
  • src/compaction/worker.rs
  • src/file.rs
  • src/table/multi_writer.rs
  • src/table/tests.rs
  • src/table/writer/filter/full.rs
  • src/table/writer/filter/mod.rs
  • src/table/writer/filter/partitioned.rs
  • src/table/writer/index/full.rs
  • src/table/writer/index/mod.rs
  • src/table/writer/index/partitioned.rs
  • src/table/writer/mod.rs
  • src/tree/ingest.rs
  • src/tree/inner.rs
  • src/tree/mod.rs
  • src/version/persist.rs
  • src/version/super_version.rs
  • src/vlog/blob_file/merge.rs
  • src/vlog/blob_file/multi_writer.rs
  • src/vlog/blob_file/reader.rs
  • src/vlog/blob_file/scanner.rs
  • src/vlog/blob_file/writer.rs
📝 Walkthrough

Walkthrough

Threads the Fs filesystem abstraction through writers, blob-file creation, version persistence, directory fsyncs, and related call sites so IO uses injected filesystem implementations instead of direct std::fs calls. (Adds fs parameters/fields and updates constructors and persist/upgrade call sites.)

Changes

Cohort / File(s) Summary
Table writer & multi-writer
src/table/writer/mod.rs, src/table/multi_writer.rs, src/table/tests.rs
Make Writer/MultiWriter generic over FS: Fs, add fs: Arc<FS> and new constructor arg; open/create/sync/remove use Fs APIs; tests updated to pass StdFs.
Blob/vlog writer & multi-writer
src/vlog/blob_file/writer.rs, src/vlog/blob_file/multi_writer.rs, src/vlog/blob_file/{reader,scanner,merge}.rs
Make blob Writer/MultiWriter generic over FS: Fs, thread fs into constructors/rotation/consume_writer and use fs for create/remove/sync; tests updated to pass StdFs.
Filter & index writers
src/table/writer/filter/{mod,full,partitioned}.rs, src/table/writer/index/{mod,full,partitioned}.rs
Generalize writer types by tightening trait bounds to Write + Seek and replace concrete BufWriter<File> with generic W/WR so FS-backed writers can be used.
Filesystem utilities
src/file.rs
rewrite_atomic and fsync_directory now accept fs: &impl Fs and use fs.open/FsFile::sync_all/fs.sync_directory; tests updated.
Version persist & upgrade
src/version/persist.rs, src/version/super_version.rs
persist_version and SuperVersions::upgrade_version(_with_seqno) accept fs: &impl Fs; persistence, atomic rewrite, and dir sync use the injected Fs.
Tree & blob_tree init / ingest
src/tree/{mod,inner}.rs, src/tree/ingest.rs, src/blob_tree/{mod,ingest}.rs
Use config.fs for directory creation/fsync and thread config.fs into MultiWriter::new/BlobFileWriter::new and version-upgrade/persist calls.
Compaction & worker paths
src/compaction/{flavour,filter,worker}.rs
Thread opts.config.fs into MultiWriter/BlobFileWriter constructions and into upgrade_version(...) calls used during compaction and table movement.
Version manifest & tree init (calls)
src/tree/inner.rs, src/version/persist.rs, src/version/super_version.rs
Initial version persist and manifest rewrite now use injected Fs during tree creation and version file writes; upgrade_version plumbing updated accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Component (Compaction / Ingest / Tree)
    participant Writer as Table/Blob Writer
    participant Version as SuperVersions
    participant FS as Fs implementation
    participant Disk as Underlying storage

    Client->>Writer: new(..., fs.clone())
    Writer->>FS: open(path, FsOpenOptions { write, create_new })
    FS->>Disk: create file
    Writer->>FS: write(data)
    Writer->>FS: FsFile::sync_all()
    Client->>Version: upgrade_version(..., fs)
    Version->>FS: persist_version(folder, manifest, comparator, fs)
    FS->>Disk: write version file
    FS->>Disk: sync directory
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and concisely describes the main refactoring: threading the Fs abstraction through table::Writer and BlobFile creation.
Linked Issues check ✅ Passed All coding requirements from issue #91 are met: Fs abstraction is threaded through table::Writer, BlobFile creation, rewrite_atomic(), fsync_directory(), persist_version(), and upgrade_version() across all call sites.
Out of Scope Changes check ✅ Passed All changes are directly scoped to threading Fs through the write path. No unrelated modifications or scope creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description clearly explains the objectives and changes: generalizing traits, making writers generic over Fs, threading Fs through functions, and eliminating direct std::fs dependencies from the write path to enable io_uring and per-level filesystem selection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil

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

Copy link
Copy Markdown

Copilot AI left a comment

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 refactors the write path to consistently use the crate’s Fs abstraction instead of hardcoded std::fs, enabling alternative filesystem backends (e.g., io_uring-oriented) and future per-level filesystem routing.

Changes:

  • Generalizes table index/filter writer traits to accept a generic writer type (instead of std::fs::File-specific finish() signatures).
  • Makes table writers and blob-file writers generic over FS: Fs = StdFs, and threads fs through version persistence and atomic rewrite helpers.
  • Updates call sites across tree creation/ingestion/compaction and tests to pass config.fs through.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/vlog/blob_file/writer.rs Makes blob writer generic over FS and switches file creation/sync to Fs/FsFile.
src/vlog/blob_file/multi_writer.rs Threads fs through blob multi-writer lifecycle and deletion paths.
src/vlog/blob_file/scanner.rs Updates tests to construct writers with StdFs.
src/vlog/blob_file/reader.rs Updates tests to pass an Arc<StdFs> into blob writer construction.
src/vlog/blob_file/merge.rs Updates tests to construct writers with StdFs.
src/table/writer/mod.rs Makes table writer generic over FS, uses Fs for create/remove/sync, and updates writer trait object types.
src/table/writer/index/* Generalizes index writer trait and implementations to accept W: Write + Seek.
src/table/writer/filter/* Generalizes filter writer trait and implementations to accept W: Write + Seek.
src/table/multi_writer.rs Makes table multi-writer generic over FS and passes fs into rotated writers.
src/table/tests.rs Updates table tests to construct writers with Arc<StdFs>.
src/file.rs Threads Fs through rewrite_atomic() and fsync_directory() for directory syncing.
src/version/persist.rs Threads Fs through version persistence and atomic rewrite of CURRENT.
src/version/super_version.rs Threads Fs through version upgrade/persist call chain.
src/tree/mod.rs Passes config.fs into writer/version upgrade paths and directory fsyncs.
src/tree/inner.rs Passes config.fs into initial version persistence.
src/tree/ingest.rs Passes config.fs into ingestion writers and version upgrade paths.
src/compaction/* Passes config.fs into compaction writers and version upgrade paths.
src/blob_tree/* Passes config.fs into blob-tree writers and version upgrade paths.

Comment thread src/vlog/blob_file/multi_writer.rs Outdated
Comment thread src/vlog/blob_file/writer.rs Outdated
@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from 53a280e to 8e1dce1 Compare March 22, 2026 19:54
Copy link
Copy Markdown

@sw-release-bot sw-release-bot Bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'lsm-tree db_bench'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.15.

Benchmark suite Current: 780bbf9 Previous: d7279d3 Ratio
readseq 2336815.33618255 ops/sec 3054133.461936785 ops/sec 1.31
mergerandom 644995.6304771013 ops/sec 784591.1446116187 ops/sec 1.22

This comment was automatically generated by workflow using github-action-benchmark.

CC: @polaz

@polaz polaz requested a review from Copilot March 22, 2026 19:58
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/table/writer/index/mod.rs:1

  • Requiring W: Write + Seek at the trait level forces all backends (and thus FS::File used by table writers) to be seekable, which can block non-seekable write-oriented implementations (e.g., some io_uring-style or append-only abstractions). If only some implementations require seeking, consider moving the Seek bound to the specific methods/implementations that need it (e.g., via method-level where W: Seek), or splitting into separate traits for seek-free vs seek-required index writers.
// Copyright (c) 2024-present, fjall-rs

Comment thread src/table/writer/mod.rs Outdated
Comment thread src/version/persist.rs Outdated
@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from 8e1dce1 to 4935dfb Compare March 22, 2026 20:14
@polaz polaz requested a review from Copilot March 22, 2026 20:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/table/writer/mod.rs:1

  • This bypasses the existing file::fsync_directory(...) wrapper which is explicitly a no-op on Windows. Unless Fs::sync_directory is guaranteed to be Windows-safe/no-op by contract (including for non-StdFs implementations), this can cause Windows failures/regressions. Consider calling crate::file::fsync_directory(parent, &*self.fs) here (or adding a cfg/no-op guarantee to the Fs trait contract) to preserve cross-platform behavior.
// Copyright (c) 2024-present, fjall-rs

Comment thread src/table/writer/mod.rs Outdated
Comment thread src/table/writer/mod.rs Outdated
Comment thread src/table/writer/mod.rs Outdated
Comment thread src/table/writer/mod.rs Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comment thread src/table/writer/mod.rs Outdated
Comment thread src/vlog/blob_file/multi_writer.rs Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 22, 2026

Codecov Report

❌ Patch coverage is 97.11538% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/tree/mod.rs 90.90% 2 Missing ⚠️
src/version/persist.rs 87.50% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from 0bef6cc to 10f2477 Compare March 22, 2026 21:49
@polaz polaz requested a review from Copilot March 22, 2026 22:07
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Comment thread src/table/writer/mod.rs Outdated
@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from f2769c8 to 708bfa9 Compare March 22, 2026 22:28
@polaz polaz requested a review from Copilot March 22, 2026 22:30
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Comment thread src/table/writer/mod.rs Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Comment thread src/table/writer/mod.rs Outdated
Comment thread src/table/writer/mod.rs Outdated
@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from e0c749d to ff78409 Compare March 23, 2026 00:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 1 comment.

Comment thread src/version/persist.rs
polaz added 16 commits March 23, 2026 19:12
- Generalize BlockIndexWriter/FilterWriter traits to use generic W
  instead of hardcoded std::fs::File in finish() methods
- Make table::Writer<FS: Fs = StdFs> generic, use Fs::open() for file
  creation, Fs::sync_directory() and Fs::remove_file() in finish()
- Make table::MultiWriter<FS: Fs = StdFs> generic, pass Fs through
- Make vlog::blob_file::Writer<FS: Fs = StdFs> generic, use Fs::open()
- Make vlog::blob_file::MultiWriter<FS: Fs = StdFs> generic
- Thread Fs through rewrite_atomic(), fsync_directory(), persist_version()
  and upgrade_version()/upgrade_version_with_seqno()
- Update all call sites to pass config.fs through

Closes #91
…path

- BlobFile Writer::new now uses create_new(true) instead of create(true),
  matching the table writer pattern and preventing silent overwrites
- Add comment explaining why consume_writer read-back still uses
  std::fs::File (FileAccessor is not yet generic over Fs)
Add doc comments noting these are not public API, so the added Fs
parameter is not a breaking change for external consumers.
FsFile: Write + Seek is enforced by the Fs trait, so no explicit
where-clause is needed on Writer<FS>.
Writer is re-exported via table::mod, so the comment was misleading.
Use plain language (`implements FsFile + Write + Seek`) instead of
nested-bound syntax that reads like `FsFile: Write + Seek`.
…ectory

The helper handles the Windows no-op case, keeping behavior consistent
with all other fsync call sites.
Explain that BlockIndexWriter/FilterWriter are generic over
W = BufWriter<FS::File>, not directly over FS::File.
Blob file IDs are monotonically unique, so path collision is a bug.
Orphaned files from crashes are cleaned during recovery.
Move FsOpenOptions import inside #[cfg(not(windows))] block and add
let _ = &fs to suppress unused-variable warning on Windows where
post-persist sync is skipped.
Replace remaining std::fs::create_dir_all calls in Tree::create_new,
recover_levels, and BlobTree::open with Fs::create_dir_all for
consistency with the Fs abstraction.

Also add note about tempfile crate's std::fs dependency in
rewrite_atomic.
The Windows fsync_directory no-op doesn't use path, so prefix with _
to suppress unused-variable warnings, consistent with the _fs param.
Ensures the existence check goes through the configured Fs backend,
consistent with the create_dir_all and fsync_directory calls that follow.
Move std::path::absolute() before fs.open() so open, remove_file,
and fsync_directory all operate on the same absolute path. Previously
open() received the raw path while cleanup used the absolutized one.
Replace generic FS: Fs type parameters with Arc<dyn Fs> / &dyn Fs
throughout Writer, MultiWriter, and helpers, following the Fs trait
simplification in #109 that removed associated types in favor of
Box<dyn FsFile> returns.
Replace std::fs::read_dir with config.fs.read_dir for consistency
with the Fs abstraction. Destructure FsDirEntry directly instead
of calling std::fs::DirEntry methods.
@polaz polaz force-pushed the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch from 93eee6d to 34e94ca Compare March 23, 2026 17:13
@polaz polaz requested a review from Copilot March 23, 2026 17:46
@polaz
Copy link
Copy Markdown
Member Author

polaz commented Mar 23, 2026

@coderabbitai re-review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

@polaz Sure, I'll re-review the changes in PR #107 now!


🧠 Learnings used
Learnt from: polaz
Repo: structured-world/lsm-tree PR: 21
File: src/table/multi_writer.rs:129-206
Timestamp: 2026-03-20T11:14:05.620Z
Learning: In `structured-world/lsm-tree`, the `write_rts_to_writer` function in `src/table/multi_writer.rs` has a known limitation when `clip_range_tombstones=true` (compaction mode): RT fragments that don't intersect any output writer's KV span are silently dropped, which can lose delete-only gaps (e.g. an RT covering `[m,p)` while all output tables cover `[a,l]` and `[q,z]`). This limitation is intentionally deferred to issue `#32` and is documented as a Known Limitation in PR `#21`. Do not flag this as a blocking concern in PR `#21` or any PR until `#32` is addressed.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 100
File: tests/custom_comparator_compaction.rs:421-447
Timestamp: 2026-03-22T20:07:13.916Z
Learning: In `structured-world/lsm-tree` (`tests/custom_comparator_compaction.rs`), the two range-scan integration tests (`reverse_comparator_range_scan_after_compaction` and the second range-scan test) are intentionally `#[ignore]`d in PR `#100` because `RunReader::new()` still uses lexicographic `range_overlap_indexes` rather than the comparator-aware variant for on-disk range reads. Threading the `SharedComparator` through `RunReader` is a known deferred limitation documented in the PR body and tracked as a follow-up. Do not flag the ignored range-scan tests or the absence of comparator-aware `RunReader` overlap logic as a blocking concern in PR `#100` or future PRs until that follow-up issue is addressed.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 21
File: src/table/writer/mod.rs:394-463
Timestamp: 2026-03-20T20:05:21.453Z
Learning: In `structured-world/lsm-tree` (`src/table/writer/mod.rs`), the `key#min`/`key#max` widening for RT bounds in `Writer::finish()` is intentionally limited to the `item_count == 0` (RT-only) branch. For mixed KV+RT tables, `MultiWriter::write_rts_to_writer` (lines 167–199 of `src/table/multi_writer.rs`) already widens `meta.first_key`/`meta.last_key` conservatively in flush mode (`clip=false`). In compaction mode (`clip=true`), RTs are intersected with the KV key range so no widening is needed. Do not flag the absence of unconditional widening in `Writer::finish()` as a bug in PR `#21` or future PRs.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-20T08:17:06.298Z
Learning: In `structured-world/lsm-tree`, the switch from `Mutex<IntervalTree>` to `RwLock<IntervalTree>` (or a freeze-on-seal immutable structure) for memtable range-tombstone storage in `src/memtable/mod.rs` is intentionally deferred from PR `#21` to a separate issue (`#30`). Do not flag the existing `Mutex<IntervalTree>` usage in `src/memtable/mod.rs` as a concurrency concern in the context of PR `#21`; it is tracked in issue `#30`.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 67
File: src/memtable/mod.rs:72-77
Timestamp: 2026-03-22T12:26:47.795Z
Learning: In `structured-world/lsm-tree` (`src/memtable/interval_tree.rs` and `src/memtable/mod.rs`), the memtable `IntervalTree` used for range tombstone containment queries (`is_key_suppressed_by_range_tombstone`, `query_suppression`, `insert_range_tombstone`) still uses lexicographic `Ord` on `UserKey` rather than the pluggable `SharedComparator` introduced in PR `#67`. Threading the comparator into the AVL interval tree is a non-trivial refactor intentionally deferred from PR `#67` and tracked in issue `#81`. Do not flag the absence of comparator-aware RT suppression in the memtable interval tree as a blocking concern in PR `#67` or future PRs until issue `#81` is addressed.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 117
File: src/tree/ingest.rs:325-330
Timestamp: 2026-03-22T22:44:15.807Z
Learning: In `structured-world/lsm-tree` (`src/tree/ingest.rs`), the write-path monotonicity guards in `Ingestion::write`, `write_tombstone`, `write_weak_tombstone`, and `write_indirection` use plain lexicographic `key > *prev` comparisons instead of the configured `UserComparator`. This is a pre-existing issue acknowledged as valid but intentionally out of scope for PR `#98` (issue `#98` covers `Run::push()` and related methods). It is tracked in a separate follow-up issue. Do not flag the lexicographic ordering guards in `src/tree/ingest.rs` ingestion write paths as a concern in PR `#98` or future PRs until that separate issue is addressed.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 108
File: src/compaction/leveled/test.rs:681-727
Timestamp: 2026-03-23T00:40:18.897Z
Learning: In `structured-world/lsm-tree` (`src/compaction/leveled/test.rs`), the regression test `multi_level_sparse_keyspace_data_integrity` (added in PR `#108` for issue `#72`) uses full-span SSTs (writes both "a" and "z" in each flush) rather than genuinely disjoint key ranges. This is a known, accepted coverage gap: at unit-test data volumes the force-trivial-move scoring (99.99) cascades all intermediate levels to Lmax, making it structurally impossible to simultaneously populate L1 and L2 in a unit test. The test verifies data integrity (read-back correctness) only, not the per-range L2 sparse-overlap optimization. Do not flag the full-span flush pattern in this test as a deficiency in PR `#108` or future PRs.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-20T08:17:39.855Z
Learning: In `structured-world/lsm-tree`, the `Memtable` range-tombstone storage uses `Mutex<IntervalTree>` (introduced in PR `#21`). Switching to `RwLock<IntervalTree>` or a freeze-on-seal immutable structure is a deliberate future improvement tracked in a separate issue — do not flag the `Mutex` usage as a blocking concern in PR `#21` or future PRs that reference this design.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-20T08:17:39.855Z
Learning: In `structured-world/lsm-tree`, the `Memtable` range-tombstone storage uses `Mutex<IntervalTree>` (introduced in PR `#21`). Switching to `RwLock<IntervalTree>` or a freeze-on-seal immutable structure is a deliberate future improvement tracked in issue `#31` — do not flag the `Mutex` usage as a blocking concern in PR `#21` or future PRs that reference this design.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 28
File: src/abstract_tree.rs:128-129
Timestamp: 2026-03-22T03:19:33.840Z
Learning: In `structured-world/lsm-tree` (`src/abstract_tree.rs`), the flush path (`AbstractTree::flush`) intentionally does NOT pass range tombstones into `CompactionStream` for RT-boundary-aware merge resolution. Flush is a co-location pass: both KV entries (including merged operands) and RTs are written together into the output SST. RT suppression is enforced exclusively at read time via `is_suppressed_by_range_tombstones` (point reads) and `RangeTombstoneFilter` (range scans). Do not flag the absence of RT propagation into `CompactionStream` during flush as a merge-semantics bug in PR `#28` or future PRs.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 12
File: src/table/block/mod.rs:109-119
Timestamp: 2026-03-15T16:10:30.764Z
Learning: In `structured-world/lsm-tree`, pre-allocation bounds checks on `uncompressed_length`, `data_length`, `handle.size()`, and `real_val_len` (256 MiB cap, returning `DecompressedSizeTooLarge`) are intentionally deferred to PR `#7` (branch `feat/#258-security-validate-uncompressedlength-before-decomp`, closes issues `#258` and `#257`). Do not flag missing size-cap guards in `Block::from_reader`, `Block::from_file`, or `blob_file::Reader::get` as unaddressed — they are handled in that separate security PR.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-23T00:44:56.036Z
Learning: In `structured-world/lsm-tree`, issue `#83` has been resolved in PR `#102` (`feat/#83-perf-extend-bloommaycontainkeyhash-to-support-part`). `Table::bloom_may_contain_key(key, key_hash)` was added to `src/table/mod.rs`, which seeks the partitioned filter TLI by user key and queries only the matching partition's bloom filter, returning `Ok(false)` for keys beyond all partition boundaries. `IterState` in `src/range.rs` gained `bloom_key: Option<UserKey>`; `bloom_passes()` dispatches to `bloom_may_contain_key` when `bloom_key` is set (single-key pipeline path) and falls back to `bloom_may_contain_key_hash` otherwise (prefix/range scans). The prior conservative `Ok(true)` fallback in `bloom_may_contain_key_hash` for partitioned/TLI filters is preserved for callers without a key. Do not flag the conservative fallback in `bloom_may_contain_key_hash` for partitioned/TLI filters as a blocking concern in any future PR — it is intentional for callers that lack a user key.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-23T11:30:24.031Z
Learning: In `structured-world/lsm-tree` (`src/fs/mod.rs`), `Fs::exists` returns `io::Result<bool>` (not `bool`), implemented via `path.try_exists()`, and propagates I/O/permission errors to the caller. This signature has been in place since it was introduced in PR `#80` and was not changed by PR `#109`. Do not flag `Fs::exists` as returning a bare `bool` or as collapsing errors to `false` in any future PR review.

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 0
File: :0-0
Timestamp: 2026-03-22T09:21:07.612Z
Learning: In `structured-world/lsm-tree` (`src/range.rs`), prefix bloom filter skipping for multi-table runs (L0) has been implemented in PR `#64` (closes issue `#57`). The `_ =>` branch in `TreeIter::create_range` now applies a per-table key-range guard followed by a bloom check; 0 survivors skips the run, 1 survivor is demoted to `single_tables` for the range-tombstone table-skip optimization, and 2+ survivors are collected into a new `Run`. Range tombstones are still collected from ALL tables in the run regardless of bloom filtering. Do not flag the absence of multi-table run prefix bloom skipping in this repository — it has been implemented.

Learnt from: CR
Repo: structured-world/lsm-tree PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-20T22:03:44.708Z
Learning: Applies to src/vlog/blob_file/**/*.rs : `src/vlog/blob_file/` — Value log for large values (separate from LSM blocks)

Learnt from: polaz
Repo: structured-world/lsm-tree PR: 45
File: tools/db_bench/src/workloads/readwhilewriting.rs:68-79
Timestamp: 2026-03-22T03:49:44.713Z
Learning: In `structured-world/lsm-tree`, the `readwhilewriting` workload in `tools/db_bench/src/workloads/readwhilewriting.rs` intentionally uses a fixed `config.num` insert loop for the writer thread, which may finish before the reader threads complete. This is by design: the benchmark measures read throughput under a fixed volume of concurrent write pressure, not sustained writes for the entire reader measurement window. Do not flag the writer exhausting its loop before readers as a bug in PR `#45` or future PRs.
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

@polaz polaz merged commit c2fe71e into main Mar 23, 2026
20 checks passed
@polaz polaz deleted the feat/#91-refactor-thread-fs-through-tablewriter-and-blobfil branch March 23, 2026 18:07
@sw-release-bot sw-release-bot Bot mentioned this pull request Mar 23, 2026
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.

refactor: thread Fs through table::Writer and BlobFile creation

2 participants