Skip to content

feat(parquet): route reader through the object-store seam (rfc0013 green) - #233

Merged
jensholdgaard merged 6 commits into
mainfrom
rfc0013-green-reader-seam
Jun 15, 2026
Merged

feat(parquet): route reader through the object-store seam (rfc0013 green)#233
jensholdgaard merged 6 commits into
mainfrom
rfc0013-green-reader-seam

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jun 15, 2026

Copy link
Copy Markdown
Owner

RFC 0013 green slice 3 — wires the sync Reader read path onto the Store seam.

What

  • Reader::open_file / open_partition read bytes through Store::local(parent).get_blocking(name)open_bytes, routing the local read path through the RFC 0013 abstraction (the same one the S3 backend will use). Extracted a shared from_bytes helper; open_bytes reuses it.
  • Store gains get_blocking / put_blocking — the sync→async bridge the sync storage layer needs (object_store is async; compaction must reach S3 per RFC0013.3, so a local-only std::fs shortcut won't do).
  • tokio promoted to a normal dep (rt, rt-multi-thread) — already in the tree via DataFusion / object_store, no Cargo.lock churn.

Bridge design (the non-obvious bit)

The sync storage code (Writer/Reader/compaction) must drive async object_store ops. block_on panics "runtime within a runtime" if called on a thread that's inside a tokio runtime — and one consumer (ourios-querier's rfc0001_time_preserved test) opens the reader from a #[tokio::test]. So the bridge runs block_on on a fresh OS thread via std::thread::scope: a plain thread never inherits the caller's tokio context, making it safe from any call site (sync test, spawn_blocking compaction sweep, or inside a runtime). scope lets the future borrow the caller's self/key. Both the spawn_blocking and inside-#[tokio::test] paths are covered by new tests.

RFC0013.2 (local regresses nothing — read half)

Every existing RFC 0005 / RFC 0009 suite passes unchanged now that reads flow through the seam (full workspace cargo test green). No on-disk format change (§3.5 untouched — identical Parquet bytes).

Scope notes

  • The querier hot path is unaffected — it reads via DataFusion's own ListingTable/object_store integration, not ourios_parquet::Reader. The only sync Reader consumers are compaction and tests.
  • The write half (Writer → Store.put) and the manifest conditional-PUT (RFC0013.3/.4) land in following slices; the §5 stubs stay #[ignore]d until then.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added synchronous blocking methods for storage operations.
    • Parquet reading can now load data via the unified storage abstraction.
  • Refactor
    • Updated file/byte opening flow to use object-storage reads rather than direct filesystem reads.
    • Improved diagnostics by retaining the originating source path.
  • Bug Fixes
    • Enhanced error reporting for runtime-related failures during sync↔async bridging.
  • Tests
    • Added coverage for blocking storage round-trips, including calls from within an async runtime.

…een)

Wire the sync `Reader` read path onto the RFC 0013 `Store` seam: `open_file`
/ `open_partition` now read bytes via `Store::local(parent).get_blocking(name)`
→ `open_bytes`, so the local read path goes through the same abstraction the
S3 backend will. Behaviour-preserving — every existing RFC 0005 / RFC 0009
suite passes unchanged through the seam (RFC0013.2, read half).

`Store` gains the sync→async bridge the sync storage layer needs (`object_store`
is async; compaction must reach S3 per RFC0013.3, so a local-only `std::fs`
shortcut won't do): `get_blocking` / `put_blocking` drive the async ops on a
shared multi-thread(1) runtime, running `block_on` on a fresh OS thread via
`std::thread::scope`. A plain thread never inherits the caller's tokio context,
so the bridge is safe even when called from inside a runtime (a `#[tokio::test]`
that reads back via `Reader` does exactly that) — `block_on` on the caller's
thread would panic "runtime within a runtime". Empirically validated both the
spawn_blocking and inside-#[tokio::test] call sites.

Invariants: no on-disk format change (§3.5 untouched — same Parquet bytes). The
querier hot path is unaffected (it reads via DataFusion's own object_store
integration, not `ourios_parquet::Reader`); the only sync consumers are
compaction and tests.

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

coderabbitai Bot commented Jun 15, 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 42 minutes and 16 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.

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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

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: d72c1d57-0185-40cb-a512-1ccf3cd2d167

📥 Commits

Reviewing files that changed from the base of the PR and between db6aead and c7faf96.

📒 Files selected for processing (2)
  • crates/ourios-parquet/src/reader.rs
  • crates/ourios-parquet/src/store.rs
📝 Walkthrough

Walkthrough

Adds a Tokio runtime bridge to Store with get_blocking/put_blocking that spawns a fresh OS thread to avoid nested-runtime panics, extends StoreError with a Runtime variant, and rewires Reader::open_file to read bytes through the Store seam instead of std::fs::File. The tokio dependency is added to Cargo.toml.

Changes

Sync→async bridge and Reader integration

Layer / File(s) Summary
Tokio runtime bridge and StoreError::Runtime
crates/ourios-parquet/Cargo.toml, crates/ourios-parquet/src/store.rs
Declares tokio (rt feature, default-features disabled) as a dependency. Adds block_on_off_runtime helper that drives a future on a spawned OS thread to avoid nested-runtime panics. Extends StoreError with Runtime(std::io::Error) variant, updates Display and Error::source. Adds Store::get_blocking and Store::put_blocking synchronous wrappers. Two new tests validate the bridge from a plain thread and from within a Tokio runtime.
Reader routing through Store seam
crates/ourios-parquet/src/reader.rs
Removes std::fs::File import, adds Store/StoreError imports. Refactors open_file to call read_object(path) then from_bytes. Refactors open_bytes and from_bytes signature to accept a caller-provided file_path parameter. Adds read_object (derives key from path file name, opens local Store, calls get_blocking) and store_io_err (maps StoreError into ReaderError::Io with preserved op/path context).

Sequence Diagram(s)

sequenceDiagram
  participant Reader as Reader::open_file
  participant read_object as read_object
  participant Store as Store::local
  participant get_blocking as Store::get_blocking
  participant block_on as block_on_off_runtime
  participant from_bytes as Reader::from_bytes

  Reader->>read_object: read_object(path)
  read_object->>Store: Store::local(parent_dir)
  Store-->>read_object: Store
  read_object->>get_blocking: get_blocking(file_name_key)
  get_blocking->>block_on: block_on_off_runtime(future)
  block_on-->>get_blocking: Vec<u8>
  get_blocking-->>read_object: Vec<u8>
  read_object-->>Reader: Vec<u8>
  Reader->>from_bytes: from_bytes(bytes, path)
  from_bytes-->>Reader: Reader
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jensholdgaard/ourios#231: Directly extends the same Store abstraction in store.rs with async put/get/delete, which this PR builds on with the blocking bridge.
  • jensholdgaard/ourios#230: Introduces the store module and Store::local that Reader and the blocking wrappers in this PR depend on.
  • jensholdgaard/ourios#232: Modifies Reader::open_bytes and the baseline column-check logic in the same reader.rs file that this PR refactors.

Poem

🐇 Hop, hop through the async maze,
A fresh OS thread sets the pace!
No "runtime within runtime" daze,
get_blocking leaps with steady grace.
The Store now guides each Parquet trace—
This bunny approves the bridge's embrace! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: routing the Reader through the object-store seam as part of RFC 0013 implementation, which aligns with all three files' modifications.
Description check ✅ Passed The PR description covers the what/why, links to RFC 0013, includes design rationale for the bridge, and explains scope. However, the checklist items (cargo fmt/clippy, tests, docs/CHANGELOG) are not explicitly marked as completed.
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.

✏️ 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 rfc0013-green-reader-seam

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 and usage tips.

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

Wires ourios_parquet::Reader’s synchronous file-open path through the RFC 0013 Store abstraction by reading file bytes via object_store (using a sync→async bridge), so the local filesystem and future S3 backends share the same read seam.

Changes:

  • Added a process-wide Tokio runtime bridge plus Store::{get_blocking, put_blocking} to support synchronous storage call sites.
  • Refactored Reader to open files by fetching bytes through Store::local(...).get_blocking(...), and introduced a shared from_bytes helper used by both open_file and open_bytes.
  • Promoted tokio to a normal dependency of ourios-parquet to support the blocking bridge.

Reviewed changes

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

File Description
crates/ourios-parquet/src/store.rs Adds sync blocking bridge over async object_store operations and tests for runtime-safety.
crates/ourios-parquet/src/reader.rs Routes open_file through the Store seam by reading bytes via the blocking bridge; refactors reader construction.
crates/ourios-parquet/Cargo.toml Adds Tokio as a direct dependency for the sync→async bridge runtime.

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

Comment thread crates/ourios-parquet/Cargo.toml Outdated
Comment thread crates/ourios-parquet/src/store.rs
Comment thread crates/ourios-parquet/src/reader.rs Outdated

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 5 comments.

Comment thread crates/ourios-parquet/src/store.rs
Comment thread crates/ourios-parquet/src/store.rs
Comment thread crates/ourios-parquet/src/reader.rs Outdated
Comment thread crates/ourios-parquet/src/reader.rs
Comment thread crates/ourios-parquet/src/store.rs
Address review on the object-store read seam:

- Fix a flaky "cannot drop a runtime in async context" panic: the shared
  OnceLock<Runtime> bridge dropped the loser's runtime during an init race on
  the caller's thread, which can be inside a #[tokio::test]. Replace it with a
  per-call current_thread runtime built, driven, and dropped entirely on the
  scoped OS thread — no shared state, no init race, no illegal drop. Also drops
  the unnecessary enable_all() (local I/O uses spawn_blocking only; the S3
  backend adds it plus the net/io/time features in its own slice).
- Flatten block_on_off_runtime to return Result<T, StoreError> directly instead
  of a nested Result unwrapped with a trailing `?` (clearer at the call sites).
- Move the §3.9 baseline-column rustdoc back onto require_baseline_columns; it
  had been stranded above read_object after the refactor.

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

Comment thread crates/ourios-parquet/src/store.rs Outdated
Comment thread crates/ourios-parquet/src/reader.rs
Comment thread crates/ourios-parquet/Cargo.toml Outdated

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/reader.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: 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 `@crates/ourios-parquet/src/store.rs`:
- Around line 49-53: The s.spawn() call in the scoped thread block will panic on
OS thread creation failure instead of returning an error. Replace the current
s.spawn() pattern with std::thread::Builder::spawn_scoped() which returns a
Result<JoinHandle> that can be mapped to handle io::Error failures. Map any
io::Error from spawn_scoped to StoreError::Runtime just like the runtime build
error on the next line. Additionally, update the documentation for the
StoreError::Runtime variant to indicate it covers both thread creation failures
and runtime build failures, since the public API methods (get_blocking,
put_blocking) document this variant as the failure surface for the sync-to-async
bridge.
🪄 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: 574b1e88-25c3-4006-aa23-1260211183bf

📥 Commits

Reviewing files that changed from the base of the PR and between 67ca392 and db6aead.

📒 Files selected for processing (3)
  • crates/ourios-parquet/Cargo.toml
  • crates/ourios-parquet/src/reader.rs
  • crates/ourios-parquet/src/store.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/ourios-parquet/src/reader.rs

Comment thread crates/ourios-parquet/src/store.rs Outdated
- Use std::thread::Builder::spawn_scoped (not Scope::spawn, which .expect()s)
  in the sync→async bridge so OS thread-creation failure surfaces as
  StoreError::Runtime instead of panicking; name the thread for observability
  and widen the variant doc to cover thread + runtime build failures.
- store_io_err wraps StoreError as the io::Error source via io::Error::other
  instead of stringifying it, keeping the backend cause inspectable through the
  error chain (and dropping an allocation).

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

Comment thread crates/ourios-parquet/src/store.rs Outdated
Comment thread crates/ourios-parquet/src/reader.rs Outdated

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 651aa24 into main Jun 15, 2026
13 checks passed
@jensholdgaard
jensholdgaard deleted the rfc0013-green-reader-seam branch June 15, 2026 21:04
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