Skip to content

feat(collector): materialize ClickHouse analytics contracts - #667

Merged
proerror77 merged 1 commit into
mainfrom
codex/canonical-parquet-clickhouse-653
Aug 9, 2026
Merged

proerror77 merged 1 commit into
mainfrom
codex/canonical-parquet-clickhouse-653

Conversation

@proerror77

@proerror77 proerror77 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Change contract

Materialize manifest-verified canonical Parquet partitions into immutable, lineage-rich ClickHouse analytics rows. The offline plan emits separate replay-event, PIT-feature, and backtest-result writers; optional remote writes require an external per-partition claim and publish complete rows only after typed inserts succeed.

Issue relationship

Closes #653

Out of scope

ClickHouse provisioning, deployment, backtest replay from ClickHouse, raw/canonical evidence replacement, live-runtime authorization, production cutover, ACK, OSS, and credential management are out of scope. No ClickHouse instance is required for validation.

Dependencies and merge order

#651 and #666 are already on main. Final code is rebased on main@a7927bbb22d0ee20613e8705272e097533a57c9b.

Focused validation

  • cargo test --manifest-path rust_hft/Cargo.toml -p hft-collector --locked passed, including 10 materializer unit tests and 7 CLI integration tests; focused 10+7 materializer tests passed again after the final no-overlap rebase.
  • cargo clippy --manifest-path rust_hft/Cargo.toml -p hft-collector --all-targets --features collector-binance --no-deps --locked -- -D warnings passed.
  • rustfmt --edition 2021 --check on the changed Rust files and git diff --check passed.
  • Counterexamples cover wrong manifest digest, corrupted Parquet, escaping artifact paths, immutable publication races, concurrent partition claims, incompatible table contracts, idempotent retry, and conflicting manifest rejection without a deployed ClickHouse instance.

Rollout and rollback

No production rollout. This is an offline/optional analytics-plane binary and does not provision or contact ClickHouse unless an endpoint and shared claim directory are explicitly supplied. Rollback is reverting this one PR.

Scope exception

Final head 1ed5191939cd76b17076bf3be9556871937af4f2 changes four scoped files and adds 2,018 non-generated lines, exceeding the 750-line threshold. Verified input admission, immutable/idempotent publication, and the three typed writer schemas form one fail-closed materialization contract; splitting them would expose partial lineage or conflict semantics. On 2026-08-09 the solo maintainer explicitly authorized this atomic exception and removed the independent-human-approval precondition. Existing independent CodeRabbit/Codex code-review and security workflow receipts have no unresolved P1/P2; all review threads are resolved. No fabricated GitHub approval is claimed.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a ClickHouse analytics materializer for replay, PIT feature, and backtest-result artifacts. It validates manifests and artifacts, creates immutable plans, preserves lineage, performs idempotent partition writes, and adds unit and integration tests.

Changes

ClickHouse analytics materialization

Layer / File(s) Summary
Contracts and materializer entry points
rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md, rust_hft/tools/collector/Cargo.toml, rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs
Documents the materialization contract, registers the binary, and dispatches replay, PIT, and backtest-result inputs.
Artifact validation and lineage derivation
rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs
Validates manifests, hashes, schemas, replay evidence, timestamps, payloads, and PIT metadata. Builds typed rows and lineage records.
Immutable plan publication and safety helpers
rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs
Publishes immutable hashed plans, accepts identical retries, rejects conflicts, and validates SQL identifiers, hashes, and filenames.
ClickHouse delivery and integration validation
rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs, rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs
Claims partitions, writes pending and complete registry states, inserts typed rows, and tests replay, PIT, backtest-result, retry, conflict, cache, and failure cases.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Materializer
  participant PlanFile
  participant ClickHouse
  CLI->>Materializer: select artifact and execution mode
  Materializer->>Materializer: validate manifest, hashes, schema, and lineage
  Materializer->>PlanFile: publish immutable JSONEachRow plan
  Materializer->>ClickHouse: insert pending registry state
  Materializer->>ClickHouse: insert typed analytics rows
  Materializer->>ClickHouse: mark partition complete
Loading

Possibly related issues

  • proerror77/monday#653 — Defines the direct canonical Parquet to ClickHouse materialization contract implemented by this change.
  • proerror77/monday#650 — Covers the broader ClickHouse materialization scope implemented by this change.

Possibly related PRs

  • proerror77/monday#661 — Produces the canonical replay Parquet artifacts consumed and validated by this materializer.
  • proerror77/monday#254 — Uses related manifest validation and immutable publication patterns for artifact workflows.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The closing relationship to issue #653 matches the stated materialization objectives and appears to complete the described contract.
Out of Scope Changes check ✅ Passed The changes, tests, and documentation remain within the stated offline ClickHouse materialization scope.
Title check ✅ Passed The title clearly identifies the collector feature that materializes ClickHouse analytics contracts.
Description check ✅ Passed The description covers every required section and provides detailed validation, scope, rollout, rollback, and exception context.
✨ Finishing Touches 💡 1
📝 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-clickhouse-653

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.

@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: 6

🧹 Nitpick comments (4)
rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md (1)

20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Correct the "three-writer inserts" wording.

A single run materializes one input kind. send_to_clickhouse inserts the pending registry row, then the data rows for that one table, then the complete registry row. It never writes all three data tables in one run. The current text suggests three data inserts per run.

📝 Proposed wording
-Remote writes claim the identity as `pending` before data rows and publish a
-`complete` registry row only after all three-writer inserts succeed; a retry
-seeing a pending claim fails closed instead of hiding a partial materialization.
+Remote writes claim the identity as `pending` before data rows and publish a
+`complete` registry row only after every data insert for that input kind
+succeeds; a retry seeing a pending claim fails closed instead of hiding a
+partial materialization.
🤖 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/CLICKHOUSE_ANALYTICS.md` around lines 20 - 22,
Update the documentation text describing the remote write sequence to state that
each run inserts data rows for one input table between the pending and complete
registry-row inserts. Remove the wording “all three-writer inserts” so it does
not imply that one run writes all three data tables, while preserving the retry
and partial-materialization behavior.
rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs (2)

454-454: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

with_capacity(manifest.rows) trusts a self-declared count.

manifest.rows comes from the manifest body. The manifest hash proves integrity, not sanity. A very large value causes one large allocation before any row is read. Cap the reservation against the Parquet row count from reader.metadata().file_metadata().num_rows(), or clamp it to a bound.

🤖 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/clickhouse-analytics-materializer.rs` at
line 454, Update the rows allocation near the materializer’s Parquet reader flow
so it does not trust manifest.rows for an unbounded reservation. Use
reader.metadata().file_metadata().num_rows() to cap the requested capacity (or
apply an equivalent safe upper bound), while preserving row collection behavior
and avoiding large allocations from malicious manifest values.

698-705: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Set explicit ClickHouse request timeouts.

reqwest::Client::new() uses reqwest’s default request timeouts, which may still allow stalled ClickHouse connections to consume the materializer process longer than wanted. Build the client with timeouts and report reqwest::Error as ClickHouse failures if no connection can complete.

🤖 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/clickhouse-analytics-materializer.rs` around
lines 698 - 705, Update the client construction in the materializer request flow
around reqwest::Client::new to use an explicit request timeout configuration,
including the required connect and overall request limits. Build the client
through the fallible builder path and propagate its reqwest::Error as the
existing ClickHouse failure type before sending the request, preserving the
current authentication and query behavior.
rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs (1)

179-189: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert partition_identity equality across rows, not just non-emptiness.

The loop checks manifest_sha256, source_revision, venue, market, symbol, and schema_version for equality against rows[0], but only checks that partition_identity is non-empty on each row, not that all rows share the same identity. Since partition_identity is a key lineage field tying rows of one partition together, assert its equality across rows too.

♻️ Proposed strengthened assertion
     for row in &rows {
         assert_eq!(row["manifest_sha256"], rows[0]["manifest_sha256"]);
         assert_eq!(row["source_revision"], rows[0]["source_revision"]);
         assert_eq!(row["venue"], "binance");
         assert_eq!(row["market"], "usdm");
         assert_eq!(row["symbol"], "BTCUSDT");
         assert_eq!(row["schema_version"], "binance-replay-parquet-v1");
         assert_eq!(row["start_time_us"], 1_000);
         assert_eq!(row["end_time_us"], 2_000);
-        assert!(!row["partition_identity"].as_str().unwrap().is_empty());
+        assert_eq!(row["partition_identity"], rows[0]["partition_identity"]);
+        assert!(!row["partition_identity"].as_str().unwrap().is_empty());
     }
🤖 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/clickhouse_analytics_materializer.rs` around
lines 179 - 189, Update the row-validation loop in the ClickHouse materializer
test to assert each row’s partition_identity equals
rows[0]["partition_identity"], while retaining the existing non-empty validation
if needed.
🤖 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/CLICKHOUSE_ANALYTICS.md`:
- Around line 61-64: Update the result manifest documentation near the existing
backtest-result metadata requirements to state that result_plan requires the
filename to be exactly <manifest-sha256>.result-manifest.json. Clarify that
arbitrary result-manifest paths are rejected, while preserving the existing
replay-manifest filename guidance.

In `@rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs`:
- Around line 587-588: The partition identity built near partition_identity is
ambiguous when venue or symbol contains a colon. Validate the caller-supplied
venue and symbol inputs to reject ':' before constructing the identity, while
preserving the existing non-empty validation and colon-delimited format.
- Around line 713-748: The pending claim around the registry read and insert is
not concurrency-safe, allowing conflicting materializations for one partition
identity. Serialize claims using a lightweight lock mechanism (or equivalent
external per-identity serialization), then read back and verify the winning
manifest_sha256 before inserting data; preserve the existing idempotent and
conflict outcomes. Document and implement the recovery procedure for stale
materialization_state="pending" rows after failed data insertion, including how
they may be safely cleared or retried.
- Around line 247-259: Update the artifact path validation before joining in the
materializer to reject absolute paths as well as relative paths containing
ParentDir components. Ensure manifest.artifact_path cannot bypass the
manifest-directory containment check, while preserving the existing
relative-path rejection and subsequent join behavior.
- Around line 64-65: Update the CLI configuration for the password field in the
argument struct so it is no longer accepted as a plain command-line value; read
it through a secure environment-variable mechanism instead, and enable Clap’s
env feature in the collector dependency configuration. Preserve the existing
password field and downstream usage while preventing exposure through process
listings and shell history.
- Around line 800-809: Update sql_literal to escape backslashes as \\ before
escaping single quotes, ensuring partition_identity values cannot alter the
ClickHouse string literal parsed by identity_query; preserve the existing
surrounding-quote behavior.

---

Nitpick comments:
In `@rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md`:
- Around line 20-22: Update the documentation text describing the remote write
sequence to state that each run inserts data rows for one input table between
the pending and complete registry-row inserts. Remove the wording “all
three-writer inserts” so it does not imply that one run writes all three data
tables, while preserving the retry and partial-materialization behavior.

In `@rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs`:
- Line 454: Update the rows allocation near the materializer’s Parquet reader
flow so it does not trust manifest.rows for an unbounded reservation. Use
reader.metadata().file_metadata().num_rows() to cap the requested capacity (or
apply an equivalent safe upper bound), while preserving row collection behavior
and avoiding large allocations from malicious manifest values.
- Around line 698-705: Update the client construction in the materializer
request flow around reqwest::Client::new to use an explicit request timeout
configuration, including the required connect and overall request limits. Build
the client through the fallible builder path and propagate its reqwest::Error as
the existing ClickHouse failure type before sending the request, preserving the
current authentication and query behavior.

In `@rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs`:
- Around line 179-189: Update the row-validation loop in the ClickHouse
materializer test to assert each row’s partition_identity equals
rows[0]["partition_identity"], while retaining the existing non-empty validation
if needed.
🪄 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: 1618514d-be10-4563-bbbb-3fdb83c43fe2

📥 Commits

Reviewing files that changed from the base of the PR and between 9b8b36b and 16b6fff.

📒 Files selected for processing (4)
  • rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs
  • rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs

Comment thread rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated

@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: 16b6fff85d

ℹ️ 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/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs Outdated
@proerror77
proerror77 force-pushed the codex/canonical-parquet-clickhouse-653 branch 2 times, most recently from b8e0503 to 37c2076 Compare August 9, 2026 03:22

@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

🤖 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/CLICKHOUSE_ANALYTICS.md`:
- Around line 22-29: Update the retry behavior description in
cex_analytics_partitions to qualify that retries are no-ops only when the
partition is complete. Explicitly retain that a retry with the same pending
manifest resumes materialization with deterministic row identities, while a
different manifest remains a conflict rejected before insertion.

In `@rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs`:
- Around line 282-294: Update
remote_write_requires_an_external_partition_claim_directory to expect
output.exists() after the command fails, preserving the assertion that stderr
contains “--claim-dir”; do not alter production ordering unless the intended
contract is to validate --claim-dir before publishing the local plan.
🪄 Autofix

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: 080316bf-0cfd-4e86-b466-b8140a85c9a1

📥 Commits

Reviewing files that changed from the base of the PR and between 16b6fff and 37c2076.

📒 Files selected for processing (4)
  • rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs
  • rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust_hft/tools/collector/src/bin/clickhouse-analytics-materializer.rs

Comment thread rust_hft/tools/collector/CLICKHOUSE_ANALYTICS.md Outdated
Comment thread rust_hft/tools/collector/tests/clickhouse_analytics_materializer.rs
@proerror77
proerror77 force-pushed the codex/canonical-parquet-clickhouse-653 branch 5 times, most recently from ec79fc8 to 1fef1c3 Compare August 9, 2026 04:53
@proerror77
proerror77 force-pushed the codex/canonical-parquet-clickhouse-653 branch from 1fef1c3 to 1ed5191 Compare August 9, 2026 05:06
@proerror77
proerror77 merged commit 67feefc into main Aug 9, 2026
46 of 47 checks passed
@proerror77
proerror77 deleted the codex/canonical-parquet-clickhouse-653 branch August 9, 2026 05:14
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: canonical Parquet → ClickHouse 分析物化契约

1 participant