Skip to content

test(parquet): rfc 0036 green — compaction properties preserved (.3) - #588

Merged
jensholdgaard merged 5 commits into
mainfrom
rfc0036-green-compaction-props
Jul 21, 2026
Merged

test(parquet): rfc 0036 green — compaction properties preserved (.3)#588
jensholdgaard merged 5 commits into
mainfrom
rfc0036-green-compaction-props

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jul 21, 2026

Copy link
Copy Markdown
Owner

RFC 0036 slice B — RFC0036.3 (compaction properties preserved)

Green slice for scenario RFC0036.3: the slice-A sorted external-merge
compaction must preserve RFC 0009's D2 (throughput) / D3 (file band)
and the §3.2 bounded-memory property. Replaces only the rfc0036_3 stub;
rfc0036_4 (green) and the rfc0036_5 / querier rfc0036_2 stubs are
untouched (still #[ignore]d, fail-on-todo when force-run). No change to the
ingest path, the manifest protocol, or slice-A's merge logic.

Memory bound — the load-bearing §3.2 claim (how it's proven)

compaction::tests::rfc0036_3_forced_spill_peak_is_one_input_not_whole_partition
measures peak decoded-row residency via a #[cfg(test)] thread-local
gauge instrumented inside sort_inputs_into / RunCursor (the counter option
in RFC 0036 §6). On a partition of K=6 inputs × S=12,000 rows it asserts the
two §3.2 paths accurately, on the same fixture:

  • Spill path (forced via the internal SortTuning { in_memory_max_bytes: 0 }
    seam): peak = one input + F×batch — phase 1 decodes inputs strictly one
    at a time; phase 2 holds one streamed batch per open run. The test fails if
    the merge ever holds the whole partition decoded
    (peak < total/2, and
    peak ≤ one_input + F·SUB_BATCH_ROWS).
  • In-memory path (in_memory_max_bytes: u64::MAX): peak = the whole
    partition
    (== K·S), the §7 skip-spill tradeoff, bounded by
    in_memory_max_bytes. The contrast (spill_peak·4 < mem_peak) is the proof.

Why a gauge, not a tracking allocator: the crate is #![deny(unsafe_code)], so
a #[global_allocator] wrapper (unsafe impl GlobalAlloc) is off-posture; and
in the RFC 0028 single it binary a process-global peak would be polluted by
concurrent tests. A compact_* call runs entirely on its caller's thread
(blocking I/O throughout), so a thread-local gauge is exact and
parallelism-proof.

D3 unchanged (structural assertion)

rfc0036_3_compaction_properties_preserved (tests/it) extends the rfc0009_1
structural style: tens of input files → exactly one live file per partition
(physically, GC'd — not manifest-hidden), rows conserved, every row group still
declares sorting_columns, decoded rows in §3.1 order. The RFC 0036 §3.3
smaller row-group threshold does not touch the file band, and D3 measures
files. The absolute 256 MiB–2 GiB size band needs baseline-corpus volume
(§9.25 / §9.7), deferred exactly as rfc0009_1 defers it.

D2 throughput band (indicative)

Measured on this PR's branch, local M-series developer machine (P-cores;
taskpolicy -B to escape the Claude-Code background-QoS E-core throttle),
release build, the ourios-bench compaction band-scale one-shot at the
exact §9.7 shape (FILES=32 ROWS=4800 BODY_BYTES=4096, ~453 MiB):

measure sorted (this run, local M-series) §9.7 unsorted (baseline-8vcpu-32gib)
D2 throughput ~138 MiB/s (137–144 over 3 runs), 153,600 rows conserved 166.8 MiB/s
D3 size band 452.7 MiB, IN 256 MiB–2 GiB, 0% < 128 MiB 456.7 MiB, 0% < 128 MiB

Honest delta. These are not the same hardware (local M-series vs the
8 vCPU EPYC-Milan baseline), so ~138 vs 166.8 MiB/s is not a clean
sorting-overhead delta — it is a fresh indicative sorted measurement, plus
the observation that adding a full external-merge-sort pass lands in the same
order of magnitude
as the unsorted copy-through and comfortably clears "keeps
up" (≫ any per-partition seal rate). The clean same-hardware sorted-vs-unsorted
delta is a validated baseline-rerun item (RFC 0031 §3.2 opt-in), not
asserted here. Recorded in docs/benchmarks.md §9.25 as a new indicative
record. The in-repo RFC0036.3 assertion is structural (D3 + memory) — no
flaky wall-clock gate (RFC 0036 §6). The criterion micro-sweep was confirmed to
still execute with the sort in the path.

Invariant / hazard note

Touches hazard #4 (small-file problem): this scenario pins the §3.2
memory property — clustering the compacted partition must not regress
compaction to whole-partition residency — and confirms the file band (D3) is
untouched
by the smaller compacted row-group threshold. Per CLAUDE.md
§6.2
, the memory bound is exercised through the real spill path via the
internal SortTuning seam (a property, driven end-to-end), not asserted by
inspection.

Verification (local, all green)

  • cargo fmt --all --check — exit 0
  • cargo clippy --all-targets --all-features -- -D warnings — clean (workspace)
  • cargo test --all-features — exit 0 (54/54 result lines, 0 failed; both
    rfc0036_3 tests pass; rfc0036_5 still ignored + fail-on-todo when force-run)
  • cargo doc --workspace --no-deps --all-features — clean
  • cargo bench -p ourios-bench --bench compaction — baseline one-shot + criterion sweep both run
  • mdbook build — clean

🤖 Generated with Claude Code

https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

Summary by CodeRabbit

  • Bug Fixes

    • Improved compaction behavior when processing large partitions, limiting peak memory usage during external sorting.
    • Verified that compaction preserves row ordering, row counts, sorting metadata, and cleanup behavior.
    • Ensured compacted partitions correctly collapse to a single committed data file.
  • Documentation

    • Added indicative benchmark results for sorted compaction, including throughput and file-size-band measurements.

RFC 0036 slice B / scenario RFC0036.3: the sorted external-merge
compaction must preserve RFC 0009's D2/D3 properties and the §3.2
bounded-memory bound. This lands the three checks.

- Memory bound (the load-bearing §3.2 claim): a thread-local
  decoded-row residency gauge (#[cfg(test)] in compaction.rs,
  instrumenting sort_inputs_into / RunCursor per RFC 0036 §6) drives
  `rfc0036_3_forced_spill_peak_is_one_input_not_whole_partition`. On a
  K-input × S-row partition it asserts the two §3.2 paths accurately:
  forced-spill peak = one input + F×batch (≪ whole partition), and the
  in-memory skip-spill path holds the whole partition (bounded by
  in_memory_max_bytes). The gauge is thread-local because a compact_*
  call runs entirely on its caller's thread, so the assertion is immune
  to `cargo test` in-process parallelism — and needs no unsafe global
  allocator (the crate is #![deny(unsafe_code)]).

- D3 unchanged: `rfc0036_3_compaction_properties_preserved` (tests/it)
  extends the rfc0009_1 structural style — tens of input files collapse
  to exactly one live file per partition, rows conserved, sorting_columns
  still declared, §3.1 order preserved. The absolute size band is the
  baseline's job (§9.25), as in rfc0009_1.

- D2 band: measured indicatively (local M-series, §9.7 shape) at
  ~138 MiB/s sorted vs the §9.7 unsorted 166.8 MiB/s (different hardware —
  not a clean delta; the authoritative sorted-vs-unsorted rerun is a
  `validated` item). D3 output lands 452.7 MiB, IN the 256 MiB–2 GiB band.
  Recorded in benchmarks.md §9.25 as a new indicative record; the in-repo
  RFC0036.3 assertion stays structural (no flaky wall-clock gate).

Hazard #4: this pins the §3.2 memory property — that clustering the
compacted partition does not regress compaction to whole-partition
residency — and confirms the file band (D3) is untouched by the
smaller compacted row-group threshold. CLAUDE.md §6.2 property-test
discipline: the memory bound is exercised through the real spill path
via the internal SortTuning seam, not asserted by inspection.

Only the rfc0036_3 stub is replaced; rfc0036_4 (green) and the
rfc0036_5 / querier rfc0036_2 stubs are untouched (still ignored,
fail-on-todo when force-run).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c1d25ee1-0f0a-4fcb-8b60-93060b12c34d

📥 Commits

Reviewing files that changed from the base of the PR and between fdc13f1 and 60ddaa1.

📒 Files selected for processing (1)
  • crates/ourios-parquet/src/compaction.rs
📝 Walkthrough

Walkthrough

Changes

RFC 0036 compaction validation

Layer / File(s) Summary
Decoded-row residency tracking
crates/ourios-parquet/src/compaction.rs
Adds test-only residency counters across spill sorting, in-memory consolidation, and merge cursor batch loading, with a forced-spill peak-residency test.
Compaction layout integration coverage
crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs
Activates comprehensive assertions for file collapse, row preservation, garbage collection, sorting metadata, and (service, time) ordering.
Sorted compaction benchmark record
docs/benchmarks.md
Documents indicative D2 throughput and D3 file-band results for sorted compaction and notes the pending authoritative rerun.

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

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is thorough but doesn't follow the required template: Summary, Related, and Checklist sections are missing. Reformat the PR body to use the template headings, add a concise Summary, Related RFC/link info, and the checkbox Checklist items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly points to RFC 0036.3 compaction-property tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rfc0036-green-compaction-props

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.

…ing comment

The forced-spill peak assertion used one_input + fan_in×SUB_BATCH_ROWS as
the bound, which is vacuous at the test's constants (F×batch=65,536 >
whole partition K×S=72,000) and the comment inverted the sizing ("S ≫
F×batch" — S=12,000 ≪ F×batch). Measured peak is exactly one input
(12,000); phase-2 opens only K<F cursors of small reader batches, well
under S. Replace the vacuous upper bound with a floor (peak ≥ one input,
formation decodes a full input) and keep the < total/2 teeth (fails on a
whole-partition regression); correct the comment to state why F×batch
does not bite here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

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

Implements the RFC 0036 slice-B “green” work for scenario RFC0036.3, adding structural + instrumentation-backed tests to ensure sorted external-merge compaction preserves RFC 0009’s D2/D3 expectations and the RFC 0036 §3.2 bounded-memory property, and records an indicative benchmark run in docs/benchmarks.md.

Changes:

  • Adds a test-only, thread-local “decoded-row residency” gauge in ourios-parquet compaction to measure peak decoded-row residency for spill vs in-memory paths.
  • Replaces the rfc0036_3 integration-test stub with a structural assertion that compaction collapses a partition to exactly one live on-disk parquet file and preserves sorted layout invariants.
  • Records an indicative D2/D3 measurement entry in docs/benchmarks.md for the sorted compaction path.

Reviewed changes

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

File Description
docs/benchmarks.md Adds an indicative benchmark record for D2/D3 after sorted compaction (RFC0036.3).
crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs Implements RFC0036.3 structural integration test validating one-output-file and sorted layout properties.
crates/ourios-parquet/src/compaction.rs Introduces test-only residency instrumentation and adds the RFC0036.3 memory-bound unit test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs Outdated
Comment thread crates/ourios-parquet/src/compaction.rs
… var (review)

Two review fixes on #588: the residency gauge's sub() now checked_sub +
panics on underflow instead of saturating (an unbalanced add/sub is a
bug in the very instrumentation the RFC0036.3 bound relies on, so it
must fail loud in test builds, not silently under-report the peak); and
the D3 test's loop binds `_` instead of an unused `f` + throwaway
`let _ = f;`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

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

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

Comment thread crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs
…filter

on_disk_parquet_count listed the whole subtree and counted every
.parquet; production live_file_keys filters to immediate children of the
partition prefix (a nested/sidecar object is not a partition file). Mirror
that so the H4 small-file-count assertion stays accurate if the layout
ever nests objects under a partition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

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

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

Comment thread crates/ourios-parquet/src/compaction.rs Outdated

@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.

🧹 Nitpick comments (1)
crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs (1)

437-472: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated production filtering logic risks silent drift.

on_disk_parquet_count/is_immediate_child hand-copy the immediate-child .parquet filtering that lives in production compaction.rs (used at several call sites there). The commit history for this very PR shows this exact duplication already drifted once ("aligned test file counting with production's immediate-child filtering"), which is a strong signal this is a real maintenance hazard, not a hypothetical one — a future change to the production filter won't automatically flow into this black-box test copy.

Consider exposing the production is_immediate_child/child-counting helper behind a test-support surface (e.g. a #[cfg(any(test, feature = "test-support"))] pub re-export) so this integration test asserts against the real implementation instead of a hand-maintained mirror.

🤖 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 `@crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs` around lines 437
- 472, Remove the duplicated filtering logic from on_disk_parquet_count and
is_immediate_child, and expose the production compaction helper or
child-counting functionality through the crate’s test-support surface using the
existing cfg conventions. Update the integration test to call that shared
production implementation while preserving the current behavior of counting only
immediate-child .parquet objects.
🤖 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.

Nitpick comments:
In `@crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs`:
- Around line 437-472: Remove the duplicated filtering logic from
on_disk_parquet_count and is_immediate_child, and expose the production
compaction helper or child-counting functionality through the crate’s
test-support surface using the existing cfg conventions. Update the integration
test to call that shared production implementation while preserving the current
behavior of counting only immediate-child .parquet objects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cf8e459-729c-4e91-81cf-26e5eef906e8

📥 Commits

Reviewing files that changed from the base of the PR and between 8329469 and fdc13f1.

📒 Files selected for processing (3)
  • crates/ourios-parquet/src/compaction.rs
  • crates/ourios-parquet/tests/it/rfc0036_write_side_layout.rs
  • docs/benchmarks.md

…t floor

Per review: the >= one_input floor baked in the current detail that
formation fully decodes an input at once. RFC0036.3's property is an
upper bound ("not whole-partition"); a future formation that streams
within an input could peak below S and still satisfy it. Drop the floor
to a > 0 gauge-liveness sanity, keep the < total/2 teeth and the ×4 <
mem_peak contrast, and rename the test to _far_below_whole_partition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y

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

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

@jensholdgaard
jensholdgaard merged commit 02de0fe into main Jul 21, 2026
26 checks passed
@jensholdgaard
jensholdgaard deleted the rfc0036-green-compaction-props branch July 21, 2026 22:58
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.

2 participants