Skip to content

feat(ingester): migrate the compactor onto the Store seam (RFC 0019 slice 2b) - #297

Merged
jensholdgaard merged 7 commits into
mainfrom
rfc0019-green-compactor-store-migration
Jun 27, 2026
Merged

feat(ingester): migrate the compactor onto the Store seam (RFC 0019 slice 2b)#297
jensholdgaard merged 7 commits into
mainfrom
rfc0019-green-compactor-store-migration

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jun 27, 2026

Copy link
Copy Markdown
Owner

What

Migrates the compactor off raw std::fs onto ourios_parquet::Store (RFC 0019 slice 2b core), so background compaction runs against a local filesystem or an S3-compatible bucket through one code path. Builds on the Store primitives (#293) and the Writer/Reader Store seam (#294).

  • compaction.rsplan_candidates, the partition/tenant walks, is_candidate, live_files, compact_partition, gc_orphans now go entirely through Store:
    • directory walks → Store::list_blocking / list_with_sizes_blocking (sizes for the small-file candidate check, no per-file stat);
    • partition discovery parses year=/month=/day=/hour= from keys, requiring a contiguous trailing canonical run (mirrors the querier's parse_day_partition fix) and skipping non-canonical keys exactly as the old dir-walk skipped non-canonical dirs;
    • inputs read via get_blockingReader::open_partition_bytes (preserves the RFC0009.5 row-vs-path abort on a mis-partitioned input);
    • output via Writer::open_in, addressed by WrittenFile.key + bytes_written (no local path stat).
  • compactor.rsCompactor holds a Store (not a PathBuf); new(store, …), run_sweep/tenants(&Store); tenants enumerated from data/tenant_id=… keys. with_audit_sink unchanged.
  • main.rs — removes the blanket s3 fail-fast; opens config.store.open() and runs the compactor + querier on either backend; pre-creates the local root before Store::local (mirrors the querier role).
  • store.rs — adds Store::supports_conditional_update() (additive backend-capability flag; true for S3, false for LocalFileSystem).

Manifest commit — backend-aware (sanity-check this)

The spec said "commit via publish_cas." In practice object_store 0.13.2's LocalFileSystem::put_opts returns NotImplemented for PutMode::Update (If-Match), so a CAS commit cannot run on the local backend — which would break RFC0019.7 (local behavior unchanged) and the inline tests. Reconciliation (matching the existing manifest.rs docs):

  • S3Manifest::publish_cas (compare-and-swap on the ETag, RFC0019.4); a lost race aborts the partition as a no-op, next sweep retries.
  • Local → atomic put_blocking overwrite — the same no-torn-read swap write_atomic performed pre-RFC-0019 (last-writer-wins, RFC0019.7 unchanged).
  • The bootstrap (create-if-absent, PutMode::Create) is shared — it works on both backends — preserving the RFC0009.3 "reader is manifest-authoritative before the consolidated file appears, no torn read" guarantee.

Gated by the new Store::supports_conditional_update().

Invariants

  • §3.5 / RFC0009.3 atomic manifest swap / no torn read — CAS (S3) or atomic put (local); bootstrap shared.
  • §3.7 / RFC0009.5 row-vs-path validation on every input via Reader::open_partition_bytes.
  • §4 hazard docs: apply RFC maturity-model amendments #4 row conservation — compaction_conserves_every_row proptest passes.
  • Crash-safety / orphan GC (rfc0009_4_*) pass; delete_blocking tolerates is_not_found (local) / idempotent delete (S3).

Known follow-ups (flagged in code; not silent breakage)

These are separate seams, deliberately out of this slice (the compactor data path is the scope here):

  1. Receiver RFC 0014 data write path is still local-only → main() fails fast if the receiver is enabled on s3 (clear error).
  2. ParquetAuditSink is still local-only → on s3 the compactor runs with a no-op audit sink and logs the gap (rather than mis-placing audit Parquet locally).

Both must land (plus the localstack e2e, slice 3) before RFC 0019 flips to green.

Tests

All existing compaction/compactor suites pass, adapted to construct via Store::local (assertions unchanged). Added an #[ignore]d localstack S3 test compact_partition_consolidates_on_s3_via_cas for the RFC0019.4 CAS commit path.

Local gate (run firsthand on the branch)

cargo fmt --all --check, cargo clippy --workspace --all-targets --all-features -D warnings, cargo test -p ourios-parquet -p ourios-ingester -p ourios-server (0 failed; 14 ignored = localstack + deferred stub), RUSTDOCFLAGS=-D warnings cargo doc — all green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Compaction and query workflows now work with both local storage and S3-backed storage.
    • Added safer compaction handling for object stores, including manifest updates and cleanup of replaced files.
  • Bug Fixes

    • Improved compaction consistency when consolidating files, reducing the chance of stale or partially updated results.
    • Fixed compaction and orphan cleanup to better handle existing manifests and missing files across storage backends.
  • Tests

    • Added end-to-end coverage for compaction on S3.

jensholdgaard and others added 2 commits June 27, 2026 22:46
Move `compact_partition`, `gc_orphans`, `plan_candidates` and their helpers
off raw `std::fs` onto `ourios_parquet::Store`, so data compaction targets a
local filesystem or an S3 bucket through one code path (RFC 0019 §3.3). Every
directory walk becomes a `Store` listing; inputs are read via `get_blocking`
+ `Reader::open_partition_bytes` (preserving the RFC0009.5 row-vs-path abort);
the consolidated file is written via `Writer::open_in` and addressed by its
object key + reported `bytes_written` (no local `stat`).

The manifest swap is backend-aware (CLAUDE.md §3.5 / RFC0009.3 — no torn read
either way): a conditional-PUT CAS (`publish_cas`) on S3 (RFC0019.4), and an
atomic overwrite on local, since `LocalFileSystem` rejects `PutMode::Update`
so `publish_cas` cannot commit there — RFC0019.7 keeps the local commit
byte-for-byte unchanged. A new `Store::supports_conditional_update()` selects
the path. The bootstrap (create-if-absent) is shared by both backends; a lost
CAS race backs off as a no-op (the consolidated file is an orphan a later
`gc_orphans` reclaims).

Partition/tenant enumeration parses canonical zero-padded Hive segments from
object keys, requiring a contiguous trailing run (mirrors the querier's
`parse_day_partition` fix) so non-canonical layouts are skipped as before.

Inline tests adapted to build a `Store::local` and pass `&store`; every
assertion preserved. Adds an `#[ignore]`d localstack S3 compaction-CAS test
(RFC0019.4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…019)

`Compactor` now holds a `Store` (built by the server from the resolved
`StoreConfig`) instead of a `bucket_root` path; `run_sweep` and `tenants`
take `&Store` and enumerate tenants from `data/tenant_id=…` object keys via
`Store::list_blocking`. `Compactor::run` clones the cheap `Store` handle into
each blocking sweep.

`main()` drops the blanket s3 fail-fast: it opens the store from
`config.store.open()` and runs the compactor on either backend, and the
querier already takes the resolved `StoreConfig`. The receiver's RFC 0014
data write path and the `ParquetAuditSink` are still local-only (their Store
migrations are later RFC 0019 slices), so the receiver fails fast on s3 and
the compaction audit sink is wired only on the local backend (s3 logs the gap
and runs without it). A local store root is pre-created before `Store::local`
canonicalises it, mirroring the querier role.

The compaction bench is updated to drive `compact_partition` through a
`Store::local`. Inline compactor tests build a `Store::local`; all assertions
preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 7 minutes and 24 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 20409679-1ad9-4839-a108-e1580db7ea59

📥 Commits

Reviewing files that changed from the base of the PR and between fc25c14 and d6b5b77.

📒 Files selected for processing (6)
  • crates/ourios-bench/benches/compaction.rs
  • crates/ourios-ingester/src/compactor.rs
  • crates/ourios-parquet/src/compaction.rs
  • crates/ourios-parquet/src/store.rs
  • crates/ourios-parquet/tests/rfc0013_object_store.rs
  • crates/ourios-server/src/main.rs
📝 Walkthrough

Walkthrough

All compaction APIs (compact_partition, gc_orphans, plan_candidates) are refactored from raw filesystem Path arguments to a Store handle. Store gains a conditional_update flag enabling CAS manifest commits on S3. Compactor stores a Store instead of a PathBuf. The server wires each role to the appropriate backend. All tests and benchmarks are updated accordingly.

Changes

Store-based compaction

Layer / File(s) Summary
Store CAS capability flag
crates/ourios-parquet/src/store.rs
Adds conditional_update: bool field (false for local, true for S3) and supports_conditional_update() method to Store.
compact_partition, gc_orphans, plan_candidates via Store
crates/ourios-parquet/src/compaction.rs
Rewrites all three public functions to use Store listing/get/put/delete; adds commit_manifest with CAS branching; removes NonUtf8FileName error variant; replaces directory-walk helpers with object-key parsing helpers; migrates all tests to Store-based setup.
Compactor and run_sweep use Store
crates/ourios-ingester/src/compactor.rs
run_sweep accepts &Store; tenants() enumerates via store.list_blocking; Compactor stores Store instead of PathBuf; Compactor::new takes Store; daemon loop clones Store into spawn_blocking; all tests migrated.
Server role wiring for S3/local backends
crates/ourios-server/src/main.rs
Local dir created only for local backend; receiver fails fast on S3; querier passes resolved store; compactor opens store via config.store.open(); ParquetAuditSink skipped for S3.
S3 integration test
crates/ourios-parquet/tests/rfc0013_object_store.rs
Adds ignored LocalStack S3 test seeding three Parquet files, running compact_partition, and asserting manifest generation 2 with one consolidated file of 3 rows.
Benchmark call-site updates
crates/ourios-bench/benches/compaction.rs
All compact_partition calls updated to pass &Store::local(dir.path()) instead of a bare path.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • jensholdgaard/ourios#239: This PR's commit_manifest CAS flow directly uses the Manifest::publish_cas / ETag conditional publish APIs introduced there.
  • jensholdgaard/ourios#294: This PR's Store-backed input streaming and output writing relies on Writer::open_in, Reader::open_partition_bytes, and WrittenFile key/size fields added there.
  • jensholdgaard/ourios#289: The server wiring changes in main.rs build directly on the StoreConfig backend resolution plumbing introduced there.

Poem

🐇 Hoppity-hop, no more bare paths to roam,
The Store now holds each parquet file at home.
CAS commits for S3, local writes stay simple,
Orphans are swept clean, not even a pimple.
The rabbit refactored, and all tests still pass — 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is informative, but it does not follow the required template and is missing the Summary, Related, and checkbox Checklist sections. Rewrite it to use the exact template headings, add a Related section with issue/RFC links, and include a checkbox checklist with the required items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the Store-seam compactor migration and matches the main change.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rfc0019-green-compactor-store-migration

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.

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 migrates the ingester compaction path off raw std::fs and onto the ourios_parquet::Store abstraction (RFC 0019 slice 2b), enabling the compactor (and server wiring) to operate on either a local filesystem store or an S3-compatible backend via a single code path.

Changes:

  • Refactors Parquet compaction planning, reading, writing, manifest publish, and orphan GC to use Store listing/get/put/delete primitives.
  • Updates the ingester compactor daemon to hold a Store, enumerate tenants via store keys, and run sweeps against either backend.
  • Updates the server main wiring to open the configured store for the compactor/querier, while still failing fast for the receiver on S3 and gating the local-only audit sink.

Reviewed changes

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

Show a summary per file
File Description
crates/ourios-server/src/main.rs Opens the configured Store for compactor/querier; keeps receiver local-only and gates the local-only audit sink.
crates/ourios-parquet/src/store.rs Adds a backend capability flag (supports_conditional_update) to gate CAS manifest commits.
crates/ourios-parquet/src/compaction.rs Migrates compaction/GC/planning from filesystem walks to store listings and store-backed reads/writes, including CAS-vs-overwrite manifest commit logic.
crates/ourios-ingester/src/compactor.rs Refactors compactor sweep orchestration to accept/hold a Store and list tenants from keys.
crates/ourios-parquet/tests/rfc0013_object_store.rs Adds an ignored LocalStack S3 integration test for CAS-backed compaction commit behavior.
crates/ourios-bench/benches/compaction.rs Updates benchmarks to call the new compact_partition(&Store, ...) API.

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

Comment thread crates/ourios-bench/benches/compaction.rs Outdated
Comment thread crates/ourios-bench/benches/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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/ourios-parquet/src/compaction.rs (1)

357-366: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restrict compaction filters to direct partition children.

Store listings are recursive, but is_committed_parquet only checks the suffix. A nested key like .../hour=10/sidecar/file.parquet would be counted as live input, compacted, or deleted as an orphan even though the writer only owns direct <uuid>.parquet children.

Proposed fix
-        let name = basename(&object);
+        let Some(name) = partition_child_name(&object, &prefix) else {
+            continue;
+        };
         // `.parquet.tmp` is always a dead interrupted publish. A `.parquet` is
@@
-        .filter(|(key, _)| is_committed_parquet(key))
+        .filter(|(key, _)| is_committed_parquet_in_partition(key, &prefix))
@@
-        .filter(|k| is_committed_parquet(k))
+        .filter(|k| is_committed_parquet_in_partition(k, &prefix))
         .collect())
 }
@@
-fn is_committed_parquet(key: &str) -> bool {
-    key.ends_with(".parquet")
+fn partition_child_name<'a>(key: &'a str, prefix: &str) -> Option<&'a str> {
+    let name = key.strip_prefix(prefix)?.strip_prefix('/')?;
+    (!name.contains('/')).then_some(name)
+}
+
+fn is_committed_parquet_in_partition(key: &str, prefix: &str) -> bool {
+    partition_child_name(key, prefix).is_some_and(|name| name.ends_with(".parquet"))
 }

Also applies to: 456-467, 551-557, 594-598

🤖 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/src/compaction.rs` around lines 357 - 366, The
compaction path is treating any recursive listing entry with a .parquet suffix
as owned input, so nested files under partition subdirectories can be mistakenly
compacted or deleted. Update the filtering logic in is_committed_parquet and the
call sites in compaction.rs to only accept direct children of the writer’s
partition/root scope (for example by validating the basename/prefix shape, not
just the suffix), and apply the same restriction wherever keys are collected or
pruned for live input, orphan detection, and deletion.
🧹 Nitpick comments (1)
crates/ourios-ingester/src/compactor.rs (1)

590-596: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the malformed-manifest test on the Store seam.

This test now re-enters the local filesystem via data_path(bucket.path()). Writing the corrupt manifest with store.put_blocking keeps the regression test aligned with the backend abstraction being introduced.

♻️ Proposed test-only refactor
-        let b_dir = PartitionKey::derive(&rec("b", 1, TS0))
-            .expect("derive")
-            .data_path(bucket.path());
-        std::fs::write(b_dir.join(ourios_parquet::MANIFEST_FILENAME), b"not json")
-            .expect("corrupt b's manifest");
+        let b_manifest = format!(
+            "data/tenant_id=b/year=2026/month=04/day=02/hour=10/{}",
+            ourios_parquet::MANIFEST_FILENAME,
+        );
+        store
+            .put_blocking(&b_manifest, b"not json".to_vec())
+            .expect("corrupt b's manifest");
🤖 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-ingester/src/compactor.rs` around lines 590 - 596, The
malformed-manifest regression test is bypassing the Store abstraction by writing
directly through the local filesystem path from
PartitionKey::derive(...).data_path(bucket.path()). Update the test in
compactor.rs to corrupt the manifest via store.put_blocking instead of
std::fs::write, so it stays on the Store seam. Keep the same manifest target
identified by PartitionKey and MANIFEST_FILENAME, but route the write through
the backend abstraction used elsewhere in the test.
🤖 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 `@crates/ourios-bench/benches/compaction.rs`:
- Around line 166-167: Move Store::local out of the timed section in the
compaction benchmarks so backend setup is excluded from the measured compaction
cost. In the benchmark cases around compact_partition, construct the Store
during setup once per temp dir (or before the iter/timed closure) and then only
invoke compact_partition(&store, &part) inside the measurement path; apply the
same change to each affected benchmark block so D2/D3/baseline keep isolating
the compaction hot path.

In `@crates/ourios-ingester/src/compactor.rs`:
- Around line 226-239: The tenant discovery logic in compactor.rs is doing a
full object listing via list_blocking(Some("data")) and then deriving tenants
from every key, which scales with all Parquet objects. Update the tenant
enumeration path in the compactor flow to use a tenant-level source instead,
such as a Store listing with delimiter/common-prefix support, a dedicated tenant
registry, or another index keyed by tenant_id, and keep the filtering/decoding
logic aligned with the existing percent_decode_tenant handling.

In `@crates/ourios-parquet/tests/rfc0013_object_store.rs`:
- Around line 533-538: The consolidation test currently only checks the row
count from Reader::open_partition_bytes/read_all, which can miss duplicate or
dropped records on the S3 path. Strengthen the assertion by comparing a stable
identifier or field set from the returned rows against the original records in
the test, using the existing rows and records values in this test block, so the
compacted object is verified as lossless rather than merely readable.

In `@crates/ourios-server/src/main.rs`:
- Around line 371-372: The compactor store is being opened too late in `main`,
after network roles may already be running, which can skip the graceful shutdown
path if `config.store.open()` fails. Preflight the store open earlier in the
startup flow before binding receiver/querier roles, then pass the opened store
handle into `Compactor::new` so the compactor only starts once the store is
guaranteed available.

---

Outside diff comments:
In `@crates/ourios-parquet/src/compaction.rs`:
- Around line 357-366: The compaction path is treating any recursive listing
entry with a .parquet suffix as owned input, so nested files under partition
subdirectories can be mistakenly compacted or deleted. Update the filtering
logic in is_committed_parquet and the call sites in compaction.rs to only accept
direct children of the writer’s partition/root scope (for example by validating
the basename/prefix shape, not just the suffix), and apply the same restriction
wherever keys are collected or pruned for live input, orphan detection, and
deletion.

---

Nitpick comments:
In `@crates/ourios-ingester/src/compactor.rs`:
- Around line 590-596: The malformed-manifest regression test is bypassing the
Store abstraction by writing directly through the local filesystem path from
PartitionKey::derive(...).data_path(bucket.path()). Update the test in
compactor.rs to corrupt the manifest via store.put_blocking instead of
std::fs::write, so it stays on the Store seam. Keep the same manifest target
identified by PartitionKey and MANIFEST_FILENAME, but route the write through
the backend abstraction used elsewhere in the test.
🪄 Autofix (Beta)

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: 16b671b5-d141-40aa-86b7-d717ffdaaec0

📥 Commits

Reviewing files that changed from the base of the PR and between f2c29be and fc25c14.

📒 Files selected for processing (6)
  • crates/ourios-bench/benches/compaction.rs
  • crates/ourios-ingester/src/compactor.rs
  • crates/ourios-parquet/src/compaction.rs
  • crates/ourios-parquet/src/store.rs
  • crates/ourios-parquet/tests/rfc0013_object_store.rs
  • crates/ourios-server/src/main.rs

Comment thread crates/ourios-bench/benches/compaction.rs
Comment thread crates/ourios-ingester/src/compactor.rs Outdated
Comment thread crates/ourios-parquet/tests/rfc0013_object_store.rs Outdated
Comment thread crates/ourios-server/src/main.rs Outdated
jensholdgaard and others added 2 commits June 27, 2026 23:07
Four review fixes on the RFC 0019 compactor-migration branch:

- bench: hoist `Store::local` into the untimed `iter_batched` setup so the D2
  / D3 paths time only `compact_partition`, not store construction.
- tenants: replace the recursive `list_blocking("data")` scan with a new
  one-level `Store::list_common_prefixes_blocking` (a `list_with_delimiter`
  roll-up over immediate `data/tenant_id=…` common-prefixes), restoring the
  original `read_dir(data/)` semantics and avoiding an S3-scale full scan.
  Adds a local unit test + an `#[ignore]`d localstack S3 test.
- S3 compaction test: assert row *identity* (value-equal, timestamp-sorted)
  against the originals, not just the count, so a drop/dup can't pass.
- main: preflight `config.store.open()?` before binding the receiver/querier
  roles, so a store-open failure can't bypass their graceful shutdown; the
  opened handle is moved into `Compactor::new`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 6 out of 6 changed files in this pull request and generated 3 comments.

Comment thread crates/ourios-parquet/src/compaction.rs Outdated
Comment thread crates/ourios-parquet/src/compaction.rs
Comment thread crates/ourios-parquet/src/compaction.rs
Copilot: the Store list* calls are recursive, so the no-manifest glob fallback
(live_file_keys) and the candidate size scan (is_candidate) could fold a nested
<uuid>.parquet under the partition prefix in as a live input — a regression from
the pre-RFC-0019 read_dir, which saw immediate children only. Add is_immediate_child
and gate both scans on it. Also reword no_op_outcome's doc: it covers a lost CAS
race too, where inputs were read and a consolidated orphan written but nothing
was committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 6 out of 6 changed files in this pull request and generated 1 comment.

Comment thread crates/ourios-parquet/src/compaction.rs Outdated
Copilot: the sub-two-file case still does the listing + manifest read before
deciding it's a no-op, so 'no I/O happens' was inaccurate; reword to 'no
consolidation is performed'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread crates/ourios-server/src/main.rs Outdated
Comment thread crates/ourios-parquet/src/compaction.rs Outdated
…ive scan

`hour_partitions` listed every object under `data/tenant_id=<enc>` and parsed
partition tuples from keys — O(N_objects) per sweep on S3 and a regression
from the pre-RFC-0019 level-by-level `read_dir` walk. Walk the Hive levels
with a delimiter rollup instead: roll up `year=` child prefixes via
`Store::list_common_prefixes_blocking`, then `month=`/`day=`/`hour=` under
each, so every listing returns only the immediate common-prefixes (cheap).
Each segment is parsed in the canonical zero-padded form, dropping a
non-canonical intermediate prefix exactly as the old walk dropped
non-canonical dirs; the result is identical (sorted chronologically, deduped).
Removes the now-unused whole-key parser `parse_hour_partition_key`.

Also reword the main.rs store-open preflight comment: `open` validates only
local-root existence / config — `Store::s3` does not contact the endpoint, so
credential/connectivity errors surface on first request, not at open.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 6 out of 6 changed files in this pull request and generated no new comments.

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