Skip to content

fix(binance): restart capture epoch after depth gap - #126

Merged
proerror77 merged 2 commits into
mainfrom
codex/binance-gap-capture-epoch
Jul 18, 2026
Merged

proerror77 merged 2 commits into
mainfrom
codex/binance-gap-capture-epoch

Conversation

@proerror77

@proerror77 proerror77 commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Change contract

Any Binance order-book depth sequence gap ends the current capture epoch: the unsafe segment remains evidence, while the existing outer reconnect starts a fresh session_id and ProcessState so later replay-safe segments cannot bridge over skipped data.

Out of scope

  • Verifier, materializer, manifest schema, and replay semantics.
  • OSS publication/selection and research snapshot mapping.
  • ECS deployment, collector cutover, and all live-trading behavior.

Dependency or merge order

Based on main after #125. No stacked PR dependency follows from this change.

Focused validation

  • Real isolated ECS counterexample: snapshot lastUpdateId=11075153756947, then depth event U=11075153761705, u=11075153767256, pu=11075153761591. The old same-session resnapshot left all successor segments permanently replay_safe=false.
  • Targeted regression test depth_gap_ends_the_capture_epoch failed against the old behavior (Ok(Resnapshot("BTCUSDT"))) and passes with this change.
  • cargo test --locked -p hft-collector --features collector-binance: 159 library tests passed (2 ignored), 37 archiver tests passed, 10 materializer tests passed, and remaining bin tests passed.
  • cargo clippy --locked -p hft-collector --features collector-binance --all-targets -- -D warnings
  • cargo fmt --package hft-collector -- --check
  • git diff --check origin/main...HEAD
  • Two independent read-only reviews: no findings.

Rollout or rollback impact

The collector now uses its existing reconnect boundary after a depth gap, producing a new capture epoch and session identity. No live trading is enabled or changed. Rollback is a single PR revert; that restores same-session resnapshot behavior, after which downstream verification remains fail-closed and rejects affected successor segments.

Summary by CodeRabbit

  • New Features

    • Improved initial snapshot collection across active trading symbols.
    • Symbols without available snapshots are now explicitly excluded from capture.
    • Initial snapshot completion is reported more reliably.
  • Bug Fixes

    • Sequence gaps now surface as capture errors instead of automatically triggering resynchronization.
    • Improved handling of shutdown and snapshot-processing outcomes.

A depth gap previously resnapshotted inside the same session, leaving every later segment permanently replay-unsafe. Return the gap through the existing session failure path so the outer reconnect creates a fresh session_id and ProcessState.
@coderabbitai

coderabbitai Bot commented Jul 18, 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: 51 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: a1445166-0fd8-498f-9675-be039101d696

📥 Commits

Reviewing files that changed from the base of the PR and between 0a42fcb and d29a61c.

📒 Files selected for processing (1)
  • rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
📝 Walkthrough

Walkthrough

The Binance LOB archiver now produces snapshots for all active symbols, emits initial-snapshot completion, keeps the producer alive until shutdown, and propagates sequence-gap errors instead of scheduling resnapshots.

Changes

Binance LOB archiver control flow

Layer / File(s) Summary
Multi-symbol snapshot production
rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Snapshot requests are rate-limited across active symbols; unavailable symbols emit ExcludeSymbol, completion emits InitialSnapshotsComplete, and the producer waits for shutdown.
Direct sequence-gap propagation
rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Depth and snapshot sequence gaps now return through the generic error path, with updated capture-epoch assertions for the recorded gap count.
Session action and task control
rust_hft/tools/collector/src/bin/binance-lob-archiver.rs
Resnapshot actions and snapshot-completion task handling are removed from session control flow.

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

Sequence Diagram(s)

sequenceDiagram
  participant SnapshotProducer
  participant BinanceExchange
  participant EventChannel
  participant RunSession
  SnapshotProducer->>BinanceExchange: request snapshots for active symbols
  BinanceExchange-->>SnapshotProducer: snapshot or SnapshotUnavailable
  SnapshotProducer->>EventChannel: emit Snapshot or ExcludeSymbol
  SnapshotProducer->>EventChannel: emit InitialSnapshotsComplete
  SnapshotProducer->>RunSession: wait for shutdown
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: ending capture epochs after Binance depth gaps.
Description check ✅ Passed The description covers the required contract, out-of-scope, dependency, validation, and rollback sections with enough detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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/binance-gap-capture-epoch

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.

Caution

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

⚠️ Outside diff range comments (2)
rust_hft/tools/collector/src/bin/binance-lob-archiver.rs (2)

1207-1213: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Apply the rate limit after unavailable snapshots too.

continue bypasses the only interval wait at Lines 1232-1234. Consecutive unavailable symbols can therefore burst REST requests despite snapshot_requests_per_second, potentially triggering exchange throttling.

Proposed fix
-                        SendOutcome::Sent => continue,
+                        SendOutcome::Sent => {
+                            if wait_or_shutdown(&mut shutdown, interval).await {
+                                return Ok(TaskExit::Stopped(None));
+                            }
+                            continue;
+                        }
🤖 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-lob-archiver.rs` around lines 1207 -
1213, Update the SnapshotUnavailable branch in the symbol-processing loop so it
waits for the configured snapshot request interval before continuing, matching
the existing rate-limit behavior used by successful and other request paths.
Ensure consecutive unavailable symbols still honor snapshot_requests_per_second
instead of bypassing the interval through continue.

1207-1213: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fail the session when the final active symbol is excluded.

If every snapshot is unavailable, all states are removed and this producer remains alive. Empty states.values().all(...) checks then report the session as synced, silently collecting no market data. Terminate the session after processing the last exclusion instead.

As per coding guidelines, “Never simplify away validation, security, data-loss prevention, or other trust-boundary checks.”

Also applies to: 1236-1242

🤖 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-lob-archiver.rs` around lines 1207 -
1213, The SnapshotUnavailable handling around send_or_shutdown must terminate
the session when excluding the final active symbol. After removing the symbol
from the producer’s active state, detect that no active symbols remain and
return the appropriate failure instead of allowing empty states to satisfy the
synced check; preserve normal continuation when other symbols remain.

Source: Coding guidelines

🧹 Nitpick comments (1)
rust_hft/tools/collector/src/bin/binance-lob-archiver.rs (1)

2447-2450: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that the gap evidence is archived.

The regression verifies termination and the health counter, but not the PR’s evidence-retention contract. Add assert_eq!(segment.event_count("sequence_gap"), 1) so removing the artifact write cannot regress unnoticed.

As per coding guidelines, “Define success criteria before non-trivial work; reproduce bugs with focused tests.”

🤖 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-lob-archiver.rs` around lines 2447 -
2450, Extend the regression test assertions around the existing sequence-gap
error and process_state checks to verify evidence retention by asserting
segment.event_count("sequence_gap") equals 1. Use the existing segment symbol
and preserve the current termination and health-counter assertions.

Source: Coding guidelines

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

Outside diff comments:
In `@rust_hft/tools/collector/src/bin/binance-lob-archiver.rs`:
- Around line 1207-1213: Update the SnapshotUnavailable branch in the
symbol-processing loop so it waits for the configured snapshot request interval
before continuing, matching the existing rate-limit behavior used by successful
and other request paths. Ensure consecutive unavailable symbols still honor
snapshot_requests_per_second instead of bypassing the interval through continue.
- Around line 1207-1213: The SnapshotUnavailable handling around
send_or_shutdown must terminate the session when excluding the final active
symbol. After removing the symbol from the producer’s active state, detect that
no active symbols remain and return the appropriate failure instead of allowing
empty states to satisfy the synced check; preserve normal continuation when
other symbols remain.

---

Nitpick comments:
In `@rust_hft/tools/collector/src/bin/binance-lob-archiver.rs`:
- Around line 2447-2450: Extend the regression test assertions around the
existing sequence-gap error and process_state checks to verify evidence
retention by asserting segment.event_count("sequence_gap") equals 1. Use the
existing segment symbol and preserve the current termination and health-counter
assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 50c90222-bab3-47d3-90e4-b5101b70f741

📥 Commits

Reviewing files that changed from the base of the PR and between b2f1f80 and 0a42fcb.

📒 Files selected for processing (1)
  • rust_hft/tools/collector/src/bin/binance-lob-archiver.rs

@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: 0a42fcb967

ℹ️ 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".

state.invalidate_for_resync(budget);
return Ok(ProcessAction::Resnapshot(symbol));
}
return Err(error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the watchdog from firing during gap reconnects

When an order-book gap now takes this Err path, run_session stops all receivers before closing the segment and sleeping for the outer reconnect; unlike the old same-session resnapshot path, nothing calls mark_data() during that intentional finalization window. For large segments where close_segment/zstd runs longer than PROCESS_WATCHDOG_SECONDS (defaults are 300s zstd timeout vs 180s watchdog), the watchdog can exit the process before the unsafe evidence is finalized and before the fresh epoch starts. This path should either disarm/refresh the watchdog around intentional gap recovery or otherwise keep it from treating reconnect finalization as a market-data stall.

Useful? React with 👍 / 👎.

@proerror77
proerror77 merged commit 902d629 into main Jul 18, 2026
17 checks passed
@proerror77
proerror77 deleted the codex/binance-gap-capture-epoch branch July 19, 2026 04:27
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.

1 participant