Skip to content

feat(collector): make Rust cutover fail closed - #29

Merged
proerror77 merged 6 commits into
mainfrom
codex/rust-collector-production-runbook
Jul 15, 2026
Merged

feat(collector): make Rust cutover fail closed#29
proerror77 merged 6 commits into
mainfrom
codex/rust-collector-production-runbook

Conversation

@proerror77

@proerror77 proerror77 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • retire the deployable Python Binance LOB collector and make the governed collector Rust-only
  • add digest-addressed Tokyo Cloud Assistant install, one-hour full-catalog shadow gate, and fail-closed cutover/rollback
  • bind gate evidence to artifact, deployment bundle, and source revision; continuously enforce session/catalog/upload/freshness invariants
  • add per-spool locking, crash-recoverable upload cleanup, explicit upload-only drain, and symlink rejection
  • add CI control-plane contracts for identity, freshness, runtime health, Cloud Assistant terminal/cancel behavior, and rollback ordering

Safety

  • no production service is started by installation
  • candidate production units are enabled only after fresh health, zero restarts, and /proc/<pid>/exe verification
  • rollback clears candidate health and requires new post-restart health before enabling the prior release
  • failure leaves writer and upload units disabled and runtime-masked

Validation

  • cargo test -p hft-collector --features collector-binance --locked: 49 passed, 1 ignored live-network smoke
  • strict collector Clippy with -D warnings
  • direct Rustfmt checks
  • bash -n and ShellCheck for all Aliyun control-plane scripts
  • test-rust-lob-control-plane.sh
  • git diff --check
  • independent final spec review: no P0/P1/P2
  • independent final standards/security review: no P0/P1

Summary by CodeRabbit

  • New Features

    • Rolled out a Rust-only Binance LOB collector lane, replacing the legacy pipeline.
    • Added controlled shadow-gate → production cutover workflow with immutable evidence, plus fail-closed rollback.
    • Introduced Rust production/shadow upload units, including an upload-only pending-drain mode and broader symbol coverage with shorter segments.
  • Bug Fixes

    • Strengthened spool/artifact integrity checks (including symlink hardening) and improved post-interruption upload cleanup.
    • Added upload failure reporting to persisted health and tightened runtime/health validation policies.
  • Documentation

    • Updated the deployment README with the new Rust-only release workflow and evidence/cleanup rules.
  • Tests

    • Expanded automated gate, health, rollback, and operation-timeout validations; added CI shell script linting.

Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/lob_archiver.rs
Comment thread rust_hft/tools/collector/src/lob_archiver.rs
Comment thread rust_hft/tools/collector/src/lob_archiver.rs
@coderabbitai

coderabbitai Bot commented Jul 15, 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: 19 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

Run ID: 7c700251-512a-4069-b3cc-31b4134cd460

📥 Commits

Reviewing files that changed from the base of the PR and between 88709ab and c2a63d3.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • deployment/aliyun/test-rust-lob-control-plane.sh
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
📝 Walkthrough

Walkthrough

Replaces the Python Binance collector deployment with Rust services and a digest-pinned release workflow. Adds upload-only draining, spool-safe cleanup, shadow gating, Aliyun installation and invocation scripts, fail-closed cutover/rollback, updated health evidence, deployment configuration, documentation, and CI validation.

Changes

Rust uploader and health state

Layer / File(s) Summary
Uploader locking, cleanup, and health reporting
rust_hft/tools/collector/*
Adds --upload-only, exclusive spool locking, durable uploaded-cleanup markers, symlink-safe scanning, upload failure counts, timeout-bounded symbol discovery, and related tests.

Candidate installation and services

Layer / File(s) Summary
Digest-verified candidate installation
deployment/aliyun/deploy-rust-lob-release.sh
Bundles deployment assets, verifies hashes and source revision, installs versioned candidates remotely, and polls or cancels ECS commands.
Rust service and environment wiring
deployment/aliyun/*.service, deployment/aliyun/*.env
Adds production and upload-only units, hardens mount and filesystem access, switches shadow binaries, expands symbol coverage, and updates runtime settings.

Shadow gate and host cutover

Layer / File(s) Summary
Shadow gate and operation wrapper
deployment/aliyun/host-rust-lob-shadow-gate.sh, deployment/aliyun/invoke-rust-lob-operation.sh, deployment/aliyun/*policy.jq
Validates candidate identity, observes both markets, verifies health and OSS round trips, writes gate evidence, and exposes gated ECS operations.
Fail-closed production cutover
deployment/aliyun/host-rust-lob-cutover.sh
Validates gate evidence, drains spools, switches production symlinks and units, verifies runtime health, records cutover evidence, and restores or contains failed upgrades.

Documentation and CI

Layer / File(s) Summary
Release procedure and automated validation
deployment/aliyun/README.md, .github/workflows/ci.yml
Documents candidate installation, one-hour gating, cutover, rollback, and cleanup rules; CI runs Bash syntax checks, ShellCheck, and control-plane tests.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.64% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear and matches the main change: the Rust collector cutover is now governed to fail closed.
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 codex/rust-collector-production-runbook

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


P2 Badge Put a timeout on full-catalog discovery

Setting production to SYMBOLS=ALL makes every startup call the Rust discover_symbols path, which uses a default reqwest::Client with no request timeout before the process watchdog or health writer is started. If Binance /exchangeInfo accepts the connection but stops responding, systemd still treats this Type=simple unit as active while it collects no data and does not restart until the six-hour runtime limit. Add an explicit timeout to discovery so production and the gate fail closed instead of wedging active-but-idle.

ℹ️ 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 deployment/aliyun/host-rust-lob-cutover.sh Outdated
Comment thread deployment/aliyun/binance-lob-archiver-rust-spot.env
Comment thread deployment/aliyun/binance-lob-archiver-rust-spot.env

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

🧹 Nitpick comments (1)
deployment/aliyun/test-rust-lob-control-plane.sh (1)

126-150: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover malformed and cross-market runtime health.

Add rejection cases for string-valued catalog counts and mismatched market/dataset identity. The current tests cover only freshness and session changes.

🤖 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 `@deployment/aliyun/test-rust-lob-control-plane.sh` around lines 126 - 150,
Extend the runtime policy tests around RUNTIME_POLICY with rejection cases for
runtime-health JSON whose catalog count fields are strings instead of numbers,
and whose market/dataset identity does not match the expected identity. Assert
jq rejects each malformed or cross-market payload while preserving the existing
freshness and session checks.
🤖 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 @.github/workflows/ci.yml:
- Around line 117-122: Update the “Verify collector release control plane”
workflow step so each script matched by ../deployment/aliyun/*.sh is
individually passed to bash -n, ensuring syntax validation covers every file;
keep the existing shellcheck and test-rust-lob-control-plane.sh commands
unchanged.

In `@deployment/aliyun/binance-lob-archiver-upload`@.service:
- Around line 15-16: Remove the 600-second unit-level startup timeout from the
upload-only service by disabling TimeoutStartSec, allowing binance-lob-archiver
to drain all pending manifests while retaining the existing per-copy timeout
behavior.

In `@deployment/aliyun/deploy-rust-lob-release.sh`:
- Around line 185-186: Update the release-directory handling around the install
commands and the related lines 207-226 so a binary cannot overwrite historical
deployment evidence. Key the directory by the complete artifact, bundle, and
source-revision identity, or validate existing release.json metadata and reject
any mismatch before cp -a or mv; preserve reuse only when all identity fields
match exactly.

In `@deployment/aliyun/host-rust-lob-shadow-gate.sh`:
- Around line 189-208: Update deployment/aliyun/host-rust-lob-shadow-gate.sh
lines 189-208 to store each gate run under an immutable run/content-digested
evidence directory, removing the destructive deletion of prior gate.json and
marker files while preserving existing records. Update
deployment/aliyun/host-rust-lob-cutover.sh lines 300-329 to snapshot legacy
rollback assets into append-only cutover evidence, hash the snapshot, and never
populate an existing digest-addressed release from mutable /etc files.
- Around line 234-243: Update assert_spool_drained to detect artifact-shaped
symlinks as well as regular files, replacing the find -type f restriction with a
predicate that matches both file and symlink entries while preserving the
existing artifact-name filters and failure behavior.

In `@deployment/aliyun/invoke-rust-lob-operation.sh`:
- Around line 11-16: Extend the prerequisite-check loop in
invoke-rust-lob-operation.sh to also validate the runtime helpers seq, sleep,
and tr before execution proceeds. Reuse the existing command -v check and
missing-command error behavior so polling and decoding dependencies are verified
alongside aliyun, base64, and jq.

In `@deployment/aliyun/rust-lob-runtime-health-policy.jq`:
- Around line 1-4: The runtime health policy must reject quoted count fields
instead of accepting them in comparisons. Update the jq expression in
rust-lob-runtime-health-policy.jq to require symbol_count and
snapshot_ready_count to be numeric integers before applying the existing
thresholds and equality checks. Add a regression case in
deployment/aliyun/test-rust-lob-control-plane.sh covering quoted counts;
deployment/aliyun/host-rust-lob-cutover.sh requires no direct change.

In `@rust_hft/tools/collector/src/bin/binance-lob-archiver.rs`:
- Around line 1271-1285: Update cleanup_artifact_path and its recovery callers
to derive the expected manifest, data, and _SUCCESS filenames from the cleanup
marker filename, preferably using the marker’s segment digest. Reject metadata
names that do not exactly match those derived names before returning any
deletion path, preserving content-addressed or append-only dataset and
deployment evidence.
- Around line 1217-1229: Update the temporary cleanup-file creation in the
marker-writing flow to use exclusive, no-follow creation via
OpenOptions.create_new(true) instead of create(true).truncate(true), preventing
existing paths or symlinks from being opened. On an already-existing path, only
remove and retry when it is a stale regular file; do not delete or follow
symlinks or other file types.

---

Nitpick comments:
In `@deployment/aliyun/test-rust-lob-control-plane.sh`:
- Around line 126-150: Extend the runtime policy tests around RUNTIME_POLICY
with rejection cases for runtime-health JSON whose catalog count fields are
strings instead of numbers, and whose market/dataset identity does not match the
expected identity. Assert jq rejects each malformed or cross-market payload
while preserving the existing freshness and session checks.
🪄 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

Run ID: ee07a6bd-ccfd-4814-b06d-0797dc6c36c0

📥 Commits

Reviewing files that changed from the base of the PR and between af2eb7c and 087f134.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • .github/workflows/ci.yml
  • deployment/aliyun/README.md
  • deployment/aliyun/binance-lob-archiver-production-spot.env
  • deployment/aliyun/binance-lob-archiver-production-usdm.env
  • deployment/aliyun/binance-lob-archiver-production@.service
  • deployment/aliyun/binance-lob-archiver-rust-spot.env
  • deployment/aliyun/binance-lob-archiver-rust-upload@.service
  • deployment/aliyun/binance-lob-archiver-rust-usdm.env
  • deployment/aliyun/binance-lob-archiver-rust@.service
  • deployment/aliyun/binance-lob-archiver-upload@.service
  • deployment/aliyun/binance-lob-archiver@.service
  • deployment/aliyun/binance_lob_archiver.py
  • deployment/aliyun/deploy-rust-lob-release.sh
  • deployment/aliyun/host-rust-lob-cutover.sh
  • deployment/aliyun/host-rust-lob-shadow-gate.sh
  • deployment/aliyun/invoke-rust-lob-operation.sh
  • deployment/aliyun/rust-lob-control-plane-lib.sh
  • deployment/aliyun/rust-lob-runtime-health-policy.jq
  • deployment/aliyun/rust-lob-shadow-gate-policy.jq
  • deployment/aliyun/test-rust-lob-control-plane.sh
  • deployment/aliyun/test_binance_lob_archiver.py
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
  • rust_hft/tools/collector/src/lob_archiver.rs
💤 Files with no reviewable changes (3)

Comment thread .github/workflows/ci.yml
Comment thread deployment/aliyun/binance-lob-archiver-upload@.service Outdated
Comment thread deployment/aliyun/deploy-rust-lob-release.sh Outdated
Comment thread deployment/aliyun/host-rust-lob-shadow-gate.sh Outdated
Comment thread deployment/aliyun/host-rust-lob-shadow-gate.sh
Comment thread deployment/aliyun/invoke-rust-lob-operation.sh Outdated
Comment thread deployment/aliyun/rust-lob-runtime-health-policy.jq Outdated
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs Outdated
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs Outdated
@proerror77
proerror77 force-pushed the codex/rust-collector-production-runbook branch from 087f134 to 88709ab Compare July 15, 2026 05:51
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
@proerror77

Copy link
Copy Markdown
Owner Author

Review-fix summary for 88709ab: all actionable Codex/CodeRabbit findings are addressed; symbol discovery now has a 15-second total request timeout; release/gate/cutover evidence is immutable or append-only; forward and rollback health are bound to post-start timestamps; all production/upload/legacy units must be inactive and masked for containment. Local proof: all Aliyun Bash syntax, ShellCheck, control-plane behavior tests, rustfmt, 53 Rust tests (1 network smoke ignored), strict Clippy -D warnings, and diff-check pass. Remaining defense-in-depth only: same-UID pathname TOCTOU in a locked 0750 spool; a future dirfd/openat2 hardening can address that without blocking this rollout.

@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/src/bin/binance-lob-archiver.rs`:
- Around line 82-101: The SpoolLock::acquire implementation must safely open
only the intended regular lock file without following symlinks. Replace the
current OpenOptions path with a no-follow open, validate the resulting inode is
a regular file before locking, and add a regression test covering a symlink at
SPOOL_LOCK_FILE; apply the same protection to the corresponding lock-opening
logic near the later call site.
- Around line 1298-1312: Preflight every cleanup target produced by
cleanup_artifact_paths, including marker, before entering the deletion flow.
Permit only missing paths or regular files; reject directories and symlinks with
the existing contextual error handling, then perform removals and directory
syncs only after all validation succeeds. Update the interruption test covering
this cleanup path to assert that validation failure leaves every artifact
untouched.
🪄 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

Run ID: 24e968c8-0575-4a98-aa75-941f82e59596

📥 Commits

Reviewing files that changed from the base of the PR and between 087f134 and 88709ab.

⛔ Files ignored due to path filters (1)
  • rust_hft/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (24)
  • .github/workflows/ci.yml
  • deployment/aliyun/README.md
  • deployment/aliyun/binance-lob-archiver-production-spot.env
  • deployment/aliyun/binance-lob-archiver-production-usdm.env
  • deployment/aliyun/binance-lob-archiver-production@.service
  • deployment/aliyun/binance-lob-archiver-rust-spot.env
  • deployment/aliyun/binance-lob-archiver-rust-upload@.service
  • deployment/aliyun/binance-lob-archiver-rust-usdm.env
  • deployment/aliyun/binance-lob-archiver-rust@.service
  • deployment/aliyun/binance-lob-archiver-upload@.service
  • deployment/aliyun/binance-lob-archiver@.service
  • deployment/aliyun/binance_lob_archiver.py
  • deployment/aliyun/deploy-rust-lob-release.sh
  • deployment/aliyun/host-rust-lob-cutover.sh
  • deployment/aliyun/host-rust-lob-shadow-gate.sh
  • deployment/aliyun/invoke-rust-lob-operation.sh
  • deployment/aliyun/rust-lob-control-plane-lib.sh
  • deployment/aliyun/rust-lob-runtime-health-policy.jq
  • deployment/aliyun/rust-lob-shadow-gate-policy.jq
  • deployment/aliyun/test-rust-lob-control-plane.sh
  • deployment/aliyun/test_binance_lob_archiver.py
  • rust_hft/tools/collector/Cargo.toml
  • rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
  • rust_hft/tools/collector/src/lob_archiver.rs
💤 Files with no reviewable changes (3)
🚧 Files skipped from review as they are similar to previous changes (17)
  • deployment/aliyun/rust-lob-runtime-health-policy.jq
  • .github/workflows/ci.yml
  • deployment/aliyun/binance-lob-archiver-rust-upload@.service
  • deployment/aliyun/rust-lob-shadow-gate-policy.jq
  • deployment/aliyun/binance-lob-archiver-production-usdm.env
  • deployment/aliyun/binance-lob-archiver-rust@.service
  • deployment/aliyun/binance-lob-archiver-upload@.service
  • deployment/aliyun/rust-lob-control-plane-lib.sh
  • deployment/aliyun/binance-lob-archiver-rust-usdm.env
  • rust_hft/tools/collector/Cargo.toml
  • deployment/aliyun/binance-lob-archiver-production@.service
  • deployment/aliyun/binance-lob-archiver-rust-spot.env
  • deployment/aliyun/binance-lob-archiver-production-spot.env
  • deployment/aliyun/invoke-rust-lob-operation.sh
  • deployment/aliyun/test-rust-lob-control-plane.sh
  • deployment/aliyun/deploy-rust-lob-release.sh
  • deployment/aliyun/host-rust-lob-shadow-gate.sh

Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
@proerror77

Copy link
Copy Markdown
Owner Author

Follow-up 51457c8 closes the two late CodeRabbit Major findings. Local proof: 54 collector tests pass (25 archiver + 20 library + 9 materializer; 1 public-network smoke ignored), strict Clippy -D warnings, rustfmt, Cargo --locked, and diff-check pass.

Comment thread rust_hft/tools/collector/src/bin/binance-lob-archiver.rs Fixed
@proerror77

Copy link
Copy Markdown
Owner Author

Additional hardening in a9fd835 preflights the cleanup marker path before opening it and adds O_NONBLOCK to no-follow opens, so FIFOs/devices cannot wedge preflight. Focused symlink-lock and interrupted-cleanup tests plus strict bin Clippy pass.

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