Skip to content

fix(prediction-markets): rotate market tape on size limit instead of disabling recording - #660

Merged
proerror77 merged 7 commits into
mainfrom
codex/market-recorder-rotate-on-cap
Aug 3, 2026
Merged

proerror77 merged 7 commits into
mainfrom
codex/market-recorder-rotate-on-cap

Conversation

@proerror77

@proerror77 proerror77 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Change contract

The production Polymarket market-tape recorder (new-ploy-runner, RecordingFeed in ploy-strategy-bundles/feed/recorded.rs) treated RecordingLimits.max_bytes/max_records as "stop recording for the rest of the run" — correct for bounded backtest replay capture, wrong for 24/7 production (and the reason the deployment toml could not safely set a cap while hourly tapes reached 20–25 GB in the 2026-08-03 incident). Hitting the limit now rotates the tape through the existing writer.rotate() path, replays the active-event checkpoints in the same deterministic order as the rotate_seconds path (excluding the current EventDiscovered, which prepare_recorded_update already folded in), then appends the current update — no records lost. Fail-safe preserved: if the checkpoint replay itself exceeds the cap, or rotation cannot stage, the writer still disables (genuine misconfiguration). record_market_updates_max_bytes = 4294967296 (4 GiB) is set in deployment/aliyun/polymarket-market-tape.toml.

Issue relationship

Refs #655

Out of scope

Dependencies and merge order

None

Focused validation

  • Replaced the stops-at-limit test with recording_feed_rotates_after_record_limit_without_losing_updates (old tape keeps its records, new tape continues at sequence = 0, writer stays enabled — counterexample for the old silent-stop boundary).
  • New size_limit_rotates_tape_and_replays_active_event_checkpoints_first (4096-byte cap: fresh tape = checkpoint at seq 0, then remaining updates in order) and checkpoint_replay_beyond_the_cap_disables_the_writer (fail-safe counterexample: nothing written past the cap).
  • writer.rotate() resets next_sequence = 0 (verified recorded.rs:367), so the uploader start_sequence == 0 contract holds per rotated tape.
  • cargo test -p ploy-strategy-bundles --locked (nested workspace rust_hft/prediction-markets): 247 lib passed / 0 failed + 6/6 backtest integration; all pre-existing rotate_seconds and no-limit tests green. Clippy: zero new warnings. git diff --check clean; preflight verdict=ok.

Rollout and rollback

No runtime change until the new new-ploy-runner binary and toml value are delivered through the recorder cutover procedure; reverting the toml line restores hour-only rotation, and unset max_bytes preserves legacy single-tape behavior.

Scope exception

None

Summary by CodeRabbit

  • New Features

    • Market-update recordings now support configurable size and record-count limits.
    • Recordings can automatically rotate when a limit is reached, allowing capture to continue in a new file.
    • Rotation preserves active event context across recording boundaries where possible.
    • Automatic rotation can be enabled or disabled through configuration and remains disabled by default when unspecified.
  • Bug Fixes

    • Improved handling of recording limits, oversized checkpoints, expiration boundaries, and rotation failures.
    • Existing behavior remains available when automatic rotation is disabled.

…disabling recording

Hitting RecordingLimits.max_bytes/max_records made RecordingFeed drop its
writer and silently stop recording for the rest of the run, so the 24/7
market-tape recorder could not use the existing record_market_updates_max_bytes
knob while hourly tapes reached 20-25 GiB. A LimitReached append now rotates
through the same writer.rotate() path as rotate_seconds, replays the
active-event checkpoints into the fresh tape (excluding the current discovery,
which is appended right after), and then appends the current update, so no
records are lost and every rotated tape still starts at sequence 0. If the
checkpoint replay itself cannot fit under the cap, the existing fail-safe
still disables the writer, since that is a genuine misconfiguration.

Enable the cap in production: record_market_updates_max_bytes = 4294967296
(4 GiB) in polymarket-market-tape.toml.

Refs #655
@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: 57 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: d90f3c27-d2b4-42b7-b210-b480d38c2704

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce4417 and c1965b9.

📒 Files selected for processing (1)
  • rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs
📝 Walkthrough

Walkthrough

The PR adds optional recording rotation when record or byte limits are reached. It configures a 4 GiB limit for the Aliyun market tape and preserves stop-recording behavior when rotation is disabled or cannot complete.

Changes

Recording limit rotation

Layer / File(s) Summary
Configuration and policy wiring
rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/config.rs, deployment/aliyun/polymarket-market-tape.toml
Adds the optional rotation setting, maps it into RecordingPolicy, defaults it to disabled, and configures a 4 GiB limit with rotation enabled. Parsing, default, parity, and policy tests cover the setting.
Limit-triggered tape rotation
rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs
Adds limit-triggered tape rotation with lifecycle checkpoint replay. Tests cover record limits, byte limits, checkpoint overflow, expiration boundaries, and unchanged bounded behavior.

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

Possibly related issues

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant RuntimeConfiguration
  participant RecordingFeed
  participant TapeWriter
  RuntimeConfiguration->>RecordingFeed: provide RecordingPolicy
  RecordingFeed->>RecordingFeed: detect record or byte limit
  RecordingFeed->>TapeWriter: rotate tape
  RecordingFeed->>TapeWriter: replay lifecycle checkpoints
  RecordingFeed->>TapeWriter: append update
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states that prediction-market tapes now rotate at the size limit instead of disabling recording.
Description check ✅ Passed The description includes all required sections and provides clear behavior, scope, validation, dependencies, and rollout details.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/market-recorder-rotate-on-cap

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: b1ca63441e

ℹ️ 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/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs Outdated
Comment thread rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs Outdated
Sonic Shih added 2 commits August 3, 2026 19:14
…ckpoints before boundary updates

Existing dry-run evidence configs use record_market_updates_max_records/
max_bytes as hard caps and rely on the legacy stop-recording behavior, so
rotating on every limit broke their bounded-capture contract. Rotation is
now opt-in via record_market_updates_rotate_on_limit (default false =
legacy stop), gated in RecordingFeed::next and enabled in the production
market-tape config.

The size-limit checkpoint snapshot is now captured before the boundary
update is processed, mirroring the rotation_due path: an EventExpired
record that trips the cap no longer strips its event's EventDiscovered
checkpoint from the fresh tape. The snapshot is only taken when limits and
the flag are configured, so legacy and unlimited paths pay no per-tick
clone.

Refs #655
@proerror77

Copy link
Copy Markdown
Owner Author

Both findings addressed in 56bd0025:

  1. P1: rotate-on-limit is now opt-in — record_market_updates_rotate_on_limit (default false = legacy stop-at-cap), wired into RecordingPolicy.rotate_on_limit; the two dry-run evidence tomls keep their original hard-cap contract unchanged, production toml sets true. New test recording_feed_stops_at_record_limit_when_rotate_on_limit_is_off guards the legacy path.
  2. P2: checkpoints are now snapshotted BEFORE prepare_recorded_update whenever rotation_due || (rotate_on_limit && limits configured), so an EventExpired that trips the cap keeps its EventDiscovered checkpoint on the fresh tape (test: expired_boundary_update_keeps_its_discovered_checkpoint_on_size_rotation). Legacy/unlimited paths never clone.

@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
`@rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs`:
- Around line 588-602: Update the size-rotation snapshot gate in the
recorded-update flow to arm only when the configured record or byte limit is
within its near-cap threshold, using the existing next_sequence and
bytes_written state plus NEAR_CAP_MARGIN_BYTES. Keep the check before
prepare_recorded_update so pre-boundary capture semantics remain unchanged, and
sort event_checkpoints only when the snapshot is non-empty to avoid unnecessary
work.
🪄 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: 59ca21db-a127-42e1-abbc-78dc89257aa4

📥 Commits

Reviewing files that changed from the base of the PR and between e73d940 and 4f85812.

📒 Files selected for processing (3)
  • deployment/aliyun/polymarket-market-tape.toml
  • rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/config.rs
  • rust_hft/prediction-markets/crates/ploy-strategy-bundles/src/feed/recorded.rs

…imit rotation

The pre-boundary checkpoint snapshot was cloned and sorted on every update
whenever rotate_on_limit was set with any limit configured, which is the
production market-tape config, while a size rotation fires once per 4 GiB.
The snapshot is now taken only when rotation_due (unchanged) or when the
writer is near a configured limit: bytes_written within 1 MiB of max_bytes
(the margin must exceed the largest possible serialized record line; a
smaller max_bytes counts as always near) or next_sequence within 1 of
max_records. Far-from-cap ticks pay zero clones. Since LimitReached can
only fire when the cap is actually exceeded, the snapshot is guaranteed to
exist on the size-rotation path; a debug_assert plus a logged lazy
recompute remains as a fail-safe so checkpoints are never silently
dropped.

Refs #655
@proerror77

Copy link
Copy Markdown
Owner Author

Addressed in 89c971da: the lifecycle snapshot is now taken only when rotation_due (unchanged) or when the writer is near a configured limit (bytes_written within 1 MiB of max_bytes — with a sub-margin cap counting as always-near to preserve correctness — or next_sequence + 1 >= max_records). Far-from-cap ticks do zero clones; legacy/unlimited paths unchanged. Fail-safe: the rotation arm has a debug_assert!(near_size_limit) plus a lazy recompute with a warn! rather than silently skipping checkpoints. New tests: far_from_cap_ticks_stay_on_one_tape_without_behavior_change (no rotation, single tape) and near_cap_expired_boundary_keeps_its_discovered_checkpoint (EventExpired tripping the cap still lands its EventDiscovered checkpoint on the fresh tape). 252 lib + 6 integration green, fmt/clippy/diff-check clean.

@proerror77
proerror77 merged commit 68b11a1 into main Aug 3, 2026
45 checks passed
@proerror77
proerror77 deleted the codex/market-recorder-rotate-on-cap branch August 3, 2026 13:25
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