Skip to content

fix: recover deadlocked Binance receivers - #6

Merged
proerror77 merged 2 commits into
mainfrom
codex/binance-lob-watchdog-fix
Jul 13, 2026
Merged

fix: recover deadlocked Binance receivers#6
proerror77 merged 2 commits into
mainfrom
codex/binance-lob-watchdog-fix

Conversation

@proerror77

@proerror77 proerror77 commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bound WebSocket receiver cancellation to five seconds so a stuck close handshake cannot freeze the collector in systemd active state.
  • Treat cancellation timeout as process-fatal, allowing systemd to clean up old tasks/sockets instead of leaking them into another session.
  • Add an independent 180-second no-market-data watchdog outside the asyncio loop.
  • Arm the watchdog only after archive recovery completes, so legitimate zstd recovery (up to 300 seconds) cannot create a restart loop.
  • Expose watchdog and cancellation settings in both Spot and USD-M environments.

Root cause evidence

Both services had no network socket and no file growth after approximately 22:17, while systemd remained active. The Python main threads were idle in epoll_wait with no scheduled timeout, consistent with run_session hanging while gathering cancelled receiver tasks.

Verification

  • 29 unit/async tests pass.
  • Self-test, py_compile, and git diff --check pass.
  • Spec and adversarial re-reviews found no remaining P0/P1 blocker.
  • Tokyo runtime recovered: over 10 seconds Spot grew ~6.3 MB and USD-M grew ~4.2 MB.
  • Fresh health ages were 29s and 23s; both status=synced, sequence_gaps=0.
  • Final script and env files are installed on the Tokyo host; active services remain collecting and will load the final watchdog changes on the next restart.

Test plan

  • python3 -m unittest deployment/aliyun/test_binance_lob_archiver.py
  • python3 deployment/aliyun/binance_lob_archiver.py --self-test
  • python3 -m py_compile deployment/aliyun/binance_lob_archiver.py
  • git diff --check
  • Verify live segment growth and fresh health on Spot and USD-M

Summary by CodeRabbit

  • Bug Fixes

    • Improved recovery when market-data collection stalls or the service becomes unresponsive.
    • Added automatic process recovery after 180 seconds without market-data updates.
    • Prevented shutdown operations from hanging indefinitely by enforcing a five-second cleanup limit.
    • Confirmed successfully uploaded segments continue to be deleted, while low disk space only generates a warning.
  • Documentation

    • Clarified service recovery, watchdog, cleanup, storage, and upload behavior.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Aliyun Binance archiver now includes configurable process watchdog and bounded task-cancellation controls. It records market-data activity, exits after prolonged stalls, detects stuck shutdown tasks, and documents the recovery behavior.

Changes

Aliyun process recovery

Layer / File(s) Summary
Process-control configuration and recovery contract
deployment/aliyun/binance_lob_archiver.py, deployment/aliyun/*.env, deployment/aliyun/README.md
Adds watchdog and cancellation timeout settings, defines the cancellation exception, and documents bounded cleanup and watchdog recovery behavior.
Market-data watchdog lifecycle
deployment/aliyun/binance_lob_archiver.py, deployment/aliyun/test_binance_lob_archiver.py
Tracks the latest depth frame, arms watchdog state during collection, runs monitoring in a daemon thread, and tests the expiration deadline.
Bounded session shutdown and fatal restart
deployment/aliyun/binance_lob_archiver.py, deployment/aliyun/test_binance_lob_archiver.py
Bounds task cancellation, raises TaskCancellationStuck on timeout, records fatal health status, and tests cancellation failure handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebSocketReceiveLoop
  participant LAST_DATA_AT
  participant run_process_watchdog
  participant systemd
  WebSocketReceiveLoop->>LAST_DATA_AT: record depth-frame timestamp
  run_process_watchdog->>LAST_DATA_AT: check elapsed time
  run_process_watchdog->>systemd: exit process after watchdog deadline
Loading
sequenceDiagram
  participant run_session
  participant cancel_tasks_bounded
  participant asyncioTasks
  participant collect
  run_session->>cancel_tasks_bounded: cancel session tasks
  cancel_tasks_bounded->>asyncioTasks: await cancellation with timeout
  cancel_tasks_bounded-->>collect: raise TaskCancellationStuck
  collect->>collect: write fatal health record and re-raise
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: improving recovery for stalled Binance receivers.
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/binance-lob-watchdog-fix

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

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

await asyncio.gather(
*tasks, *resync_tasks.values(), return_exceptions=True
)
await cancel_tasks_bounded(tuple([*tasks, *resync_tasks.values()]))

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 Drain queued frames before raising cancellation timeout

When cancel_tasks_bounded times out, it raises here before the existing queue-drain loop runs. In the new stuck-close fatal path, any diff/snapshot frames already read from the websocket and sitting in queue are discarded on process restart instead of being written as archived-only/replayed-unsafe as the following drain logic intends; preserve the timeout error but flush the queued frames before re-raising it.

Useful? React with 👍 / 👎.

SPOOL_DIR.mkdir(parents=True, exist_ok=True)
recover_parts()
LAST_DATA_AT = time.monotonic()
PROCESS_WATCHDOG_ARMED = True

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 Pause the watchdog during long segment compression

Arming the process watchdog for the entire collector also leaves it active while ArchiveRuntime.rotate() is compressing a segment, even though finalize_segment() is allowed to spend up to ZSTD_TIMEOUT_SECONDS (300s) in zstd. On the full-market services, if a scheduled/shutdown rotation or compression stall applies backpressure long enough that receivers stop reading for more than PROCESS_WATCHDOG_SECONDS (180s), the watchdog takes the os._exit path and drops the in-memory queue instead of letting the normal .part recovery path preserve the segment; disarm or heartbeat the watchdog around intentional long rotations.

Useful? React with 👍 / 👎.

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

🧹 Nitpick comments (1)
deployment/aliyun/binance_lob_archiver.py (1)

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

Optional: simplify tuple construction.

tuple([*tasks, *resync_tasks.values()]) allocates an intermediate list. A direct tuple literal avoids that.

♻️ Proposed refactor
-        await cancel_tasks_bounded(tuple([*tasks, *resync_tasks.values()]))
+        await cancel_tasks_bounded((*tasks, *resync_tasks.values()))
🤖 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/binance_lob_archiver.py` at line 1031, In the task
cancellation call, replace the intermediate-list tuple construction around
cancel_tasks_bounded with a direct tuple construction combining tasks and
resync_tasks.values(), preserving the same elements and ordering.
🤖 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.

Nitpick comments:
In `@deployment/aliyun/binance_lob_archiver.py`:
- Line 1031: In the task cancellation call, replace the intermediate-list tuple
construction around cancel_tasks_bounded with a direct tuple construction
combining tasks and resync_tasks.values(), preserving the same elements and
ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12db39ec-5639-4854-a012-dea90b9defae

📥 Commits

Reviewing files that changed from the base of the PR and between a70bce1 and ca27c24.

📒 Files selected for processing (5)
  • deployment/aliyun/README.md
  • deployment/aliyun/binance-lob-archiver-spot.env
  • deployment/aliyun/binance-lob-archiver-usdm.env
  • deployment/aliyun/binance_lob_archiver.py
  • deployment/aliyun/test_binance_lob_archiver.py

@proerror77
proerror77 merged commit 4bb5ad2 into main Jul 13, 2026
21 checks passed
@proerror77
proerror77 deleted the codex/binance-lob-watchdog-fix branch July 13, 2026 15:11
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