Skip to content

feat(collector): materialize canonical replay Parquet - #661

Merged
proerror77 merged 2 commits into
mainfrom
codex/canonical-parquet-cache
Aug 3, 2026
Merged

feat(collector): materialize canonical replay Parquet#661
proerror77 merged 2 commits into
mainfrom
codex/canonical-parquet-cache

Conversation

@proerror77

@proerror77 proerror77 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Change contract

Add a fail-closed CLI that verifies selected Binance LOB raw triplets before publishing a SHA-addressed canonical replay Parquet partition and canonical manifest.

Issue relationship

Closes #651

Out of scope

Local PVC/ESSD cache warmer and hft-backtest reader (#652); ClickHouse materialization (#653); ACK/PVC/ClickHouse provisioning and every collector/runtime cutover (#654).

Dependencies and merge order

Based on main. #652 and #653 remain blocked until this contract is merged.

Focused validation

  • cargo test -p hft-collector --locked
  • cargo clippy -p hft-collector --bin binance-replay-parquet-materializer --tests --locked -- -D warnings
  • cargo metadata --locked --no-deps
  • CLI fixture proves success, ordered Parquet payload, idempotent retry, cross-directory manifest identity, conflict refusal, and corrupted-source refusal without a usable artifact.

Rollout and rollback

None. This adds an offline materialization CLI only; it changes no collector deployment, cache, ClickHouse, or runtime. Roll back by reverting c192ed7.

Scope exception

821 added lines include 27 generated Cargo.lock lines; the remaining 794 lines are one binary and its required public CLI fixture for one fail-closed behavior and rollback unit. Splitting verification, immutable publication, and the fixture would leave a non-reviewable contract. Named reviewer /root/review_standards approved this atomic exception on 2026-08-03.

Summary by CodeRabbit

  • New Features

    • Added a tool to convert Binance replay data into compressed Parquet datasets.
    • Supports market and symbol selection, validated event ordering, continuity checks, and replay metadata.
    • Produces verifiable, content-addressed artifacts with accompanying manifests and source evidence.
  • Bug Fixes

    • Rejects corrupted, incomplete, inconsistent, or conflicting source data without publishing invalid output.
    • Ensures repeated runs produce deterministic results, including after relocating source files.
  • Tests

    • Added coverage for successful conversion, validation failures, deterministic retries, artifact conflicts, and corrupted inputs.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 28 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: 11423e91-61eb-4791-be50-6102ecd32612

📥 Commits

Reviewing files that changed from the base of the PR and between c192ed7 and e3f3d21.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs
  • rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs
📝 Walkthrough

Walkthrough

Adds a Binance replay Parquet materializer. It verifies raw replay triplets, converts selected snapshots and updates into canonical events, writes immutable ZSTD-compressed Parquet artifacts, emits hashed manifests, and adds end-to-end success and corruption tests.

Changes

Binance replay materialization

Layer / File(s) Summary
Replay contracts and source verification
rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs
Defines replay structures and validates source hashes, sidecars, market selection, symbol selection, and LOB continuity.
Canonical replay event conversion
rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs
Converts snapshots and updates into ordered canonical events with validated levels and receive timestamps.
Parquet writing and immutable publication
rust_hft/tools/collector/Cargo.toml, rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs
Adds the configured Parquet dependency, writes ZSTD-compressed row groups, hashes artifacts, and publishes collision-checked output with canonical manifests.
CLI fixture and publication validation
rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs
Adds compressed fixtures and tests for successful publication, deterministic retries, conflicting artifacts, and corrupted sources.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

  • Issue 650 — Covers the canonical Binance replay Parquet materializer and content-bound artifact validation.
  • Issue 653 — Consumes the verified Parquet outputs produced by this materializer.

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Materializer
  participant RawTriplets
  participant Parquet
  participant Manifest
  CLI->>Materializer: provide mission, symbol, source paths, and hashes
  Materializer->>RawTriplets: verify triplets and continuity
  RawTriplets-->>Materializer: verified replay source
  Materializer->>Parquet: write ordered canonical events
  Parquet-->>Materializer: return artifact hash
  Materializer->>Manifest: publish artifact and source metadata
  Manifest-->>CLI: return canonical manifest
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: materializing canonical replay Parquet in the collector.
Description check ✅ Passed The description follows the required template and documents scope, dependencies, validation, rollout, rollback, and the approved scope exception.
Linked Issues check ✅ Passed The implementation and tests address issue #651 by validating raw triplets, producing ordered replay Parquet, binding manifests, and rejecting invalid or conflicting artifacts.
Out of Scope Changes check ✅ Passed The dependency, materializer, and integration tests remain within issue #651 and the stated offline materialization scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/canonical-parquet-cache

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c192ed7124

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.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: 2

🧹 Nitpick comments (3)
rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs (1)

312-315: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document that sequence is a row ordinal.

sequence is a 1-based position in the emitted tape. It is not a Binance update id. The upstream ReplaySequenceEvent exposes no exchange sequence field, so the ordinal is the only value available here.

The manifest publishes this value as sequence_start and sequence_end. A consumer can read those names as exchange sequence coverage. Add a comment here, and consider renaming the manifest fields to row_ordinal_start and row_ordinal_end, so the coverage claim stays unambiguous.

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

In `@rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs`
around lines 312 - 315, Document near the sequence calculation in the
materializer that sequence is a 1-based emitted-tape row ordinal, not a Binance
update ID, because ReplaySequenceEvent provides no exchange sequence. Rename the
manifest fields sequence_start and sequence_end to row_ordinal_start and
row_ordinal_end, updating their producers and consumers consistently.
rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs (2)

99-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the dependency on the external zstd binary.

The fixture shells out to zstd. The test fails on any machine or CI image without that binary. The failure message is a bare assertion, so the cause is not obvious. The crate already links the zstd C library through parquet's zstd feature, so a zstd dev-dependency adds no new system requirement. Compress in-process instead.

If you keep the CLI call, add a message that names the missing binary.

♻️ Compress in-process

Add the dev-dependency in rust_hft/tools/collector/Cargo.toml:

[dev-dependencies]
zstd = "0.13"

Then replace the CLI invocation:

-        let raw = directory.join("part-1.jsonl");
         let data = directory.join("part-1.jsonl.zst");
-        let mut raw_file = File::create(&raw).unwrap();
+        let mut encoder = zstd::Encoder::new(File::create(&data).unwrap(), 3).unwrap();
         for row in rows {
-            serde_json::to_writer(&mut raw_file, row).unwrap();
-            raw_file.write_all(b"\n").unwrap();
+            serde_json::to_writer(&mut encoder, row).unwrap();
+            encoder.write_all(b"\n").unwrap();
         }
-        assert!(Command::new("zstd")
-            .args(["-q", "-f"])
-            .arg(&raw)
-            .arg("-o")
-            .arg(&data)
-            .status()
-            .unwrap()
-            .success());
-        fs::remove_file(raw).unwrap();
+        encoder.finish().unwrap().sync_all().unwrap();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs` around
lines 99 - 107, Remove the external zstd Command invocation in the test fixture
and add the zstd crate as a dev-dependency in the collector Cargo.toml. Update
the surrounding materialization flow in binance_replay_parquet_materializer to
compress the raw fixture in-process through the zstd API, preserving the
existing output path and cleanup behavior.

226-232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider covering more than one row group.

The fixture produces 3 canonical rows, so write_parquet emits a single row group. The bounded row-group path in write_parquet, which chunks by ROW_GROUP_ROWS, stays untested. Add a fixture with more rows than ROW_GROUP_ROWS, or expose the bound so a test can lower it, then assert num_row_groups() and cross-row-group order.

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

In `@rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs` around
lines 226 - 232, Extend the parquet materializer test around write_parquet to
exercise multiple row groups by supplying more than ROW_GROUP_ROWS canonical
rows, or by using an exposed smaller bound. Assert the resulting
file_metadata().num_row_groups() exceeds one and verify collected rows retain
their expected order across row-group boundaries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs`:
- Around line 139-143: Update normalize_levels and ReplayPayload to retain
validated source decimal strings instead of converting values to f64, while
preserving numeric validation. Serialize bids and asks as string pairs, and
update the materializer test assertions around the JSON payload to expect those
original strings.
- Around line 451-468: Update publish_temp_immutable so the hard_link error
branch for an absent destination removes temporary before returning the original
error. Preserve the existing cleanup behavior for successful linking and when
the destination already exists, while ensuring cleanup does not replace or mask
the hard_link failure.

---

Nitpick comments:
In `@rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs`:
- Around line 312-315: Document near the sequence calculation in the
materializer that sequence is a 1-based emitted-tape row ordinal, not a Binance
update ID, because ReplaySequenceEvent provides no exchange sequence. Rename the
manifest fields sequence_start and sequence_end to row_ordinal_start and
row_ordinal_end, updating their producers and consumers consistently.

In `@rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs`:
- Around line 99-107: Remove the external zstd Command invocation in the test
fixture and add the zstd crate as a dev-dependency in the collector Cargo.toml.
Update the surrounding materialization flow in
binance_replay_parquet_materializer to compress the raw fixture in-process
through the zstd API, preserving the existing output path and cleanup behavior.
- Around line 226-232: Extend the parquet materializer test around write_parquet
to exercise multiple row groups by supplying more than ROW_GROUP_ROWS canonical
rows, or by using an exposed smaller bound. Assert the resulting
file_metadata().num_row_groups() exceeds one and verify collected rows retain
their expected order across row-group boundaries.
🪄 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: 30e80f86-d7f6-476d-a53b-26c0feaf5574

📥 Commits

Reviewing files that changed from the base of the PR and between 68b11a1 and c192ed7.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/binance-replay-parquet-materializer.rs
  • rust_hft/tools/collector/tests/binance_replay_parquet_materializer.rs

@proerror77
proerror77 merged commit 47facfd into main Aug 3, 2026
50 checks passed
@proerror77
proerror77 deleted the codex/canonical-parquet-cache branch August 3, 2026 16:13
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.

CEX: replay-safe raw triplet → canonical Parquet 分区

1 participant