Skip to content

Decay unmatched order-response buffer and exempt benign acks (fixes #218)#234

Merged
wjllance merged 1 commit into
mainfrom
fix-218-unmatched-buffer-decay
Jul 13, 2026
Merged

Decay unmatched order-response buffer and exempt benign acks (fixes #218)#234
wjllance merged 1 commit into
mainfrom
fix-218-unmatched-buffer-decay

Conversation

@wjllance

Copy link
Copy Markdown
Owner

Summary

0c076b1 started routing every order-response whose request_id did not match a pending place into unmatched_order_responses, freezing the maker (cancel all quotes + reconnect) once the buffer exceeded 256 entries. The buffer never decayed, and it had stable benign sources, so any long live session grew the count monotonically until it hit the cap and falsely failed closed:

  1. The reduce-only inventory-exit market order (cycle.rs) used .await? and discarded its submission id, so its async ack was never registered as pending and always counted as unmatched.
  2. Late acks for places already dropped by pending.retain(|p| cycle - p.cycle <= 2).
  3. Cancel acks, which legitimately carry a request_id with no matching pending place.

Changes

  • Decay (standx-maker/src/runtime.rs): unmatched entries are now stamped with the cycle they arrived and aged out after UNMATCHED_ORDER_RESPONSE_TTL_CYCLES (8). Bounded-rate benign responses can no longer grow the buffer without limit; only a sustained flood of genuinely-uncorrelated responses within the window still overflows and fails closed, preserving the real protection.
  • Exempt the inventory-exit ack (standx-cli/src/commands/maker/cycle.rs): the exit submission id is registered into pending with a sentinel level (so it never reserves a real quote slot) so its async ack correlates as matched. A reduce-only market order never rests, so reconciliation drops it when pending ages out.
  • Event OrderResponseUnmatched now carries the current cycle (standx-cli/src/commands/maker/runtime.rs).

Auth acks are consumed inside OrderResponseStream::connect() and never reach the channel, so they were already exempt.

Test plan

  • cargo fmt -- --check — no diff
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test -p standx-maker and cargo test -p standx-cli — pass
  • New regression test benign_unmatched_responses_decay_over_a_long_session drives 10,000 cycles of steady benign unmatched responses and asserts the buffer stays bounded and never freezes; the existing unmatched_response_overflow_fails_closed test confirms a same-cycle flood still fails closed.

Fixes #218

🤖 Generated with Claude Code

0c076b1 began feeding every order-response whose request_id did not match
a pending place into `unmatched_order_responses`, freezing the maker once
the buffer passed 256 entries. The buffer never decayed and had stable
benign sources — the reduce-only inventory-exit ack (its submission id was
discarded, never registered as pending), late acks for places already
dropped by `pending.retain`, and cancel acks — so any long live session
grew the count monotonically until it hit the cap and falsely failed
closed, cancelling all quotes and reconnecting.

- Age unmatched entries out by cycle (UNMATCHED_ORDER_RESPONSE_TTL_CYCLES)
  so bounded-rate benign responses can no longer grow the buffer without
  limit; only a sustained flood of genuinely-uncorrelated responses within
  the window still overflows and fails closed.
- Register the inventory-exit submission id into `pending` (sentinel level
  so it never reserves a quote slot) so its async ack correlates instead of
  counting as unmatched.
- Add a long-session regression test proving the buffer stays bounded and
  never freezes under a steady stream of benign unmatched responses, while
  a same-cycle flood still fails closed.

Fixes #218

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@wjllance
wjllance force-pushed the fix-218-unmatched-buffer-decay branch from b97eb54 to 527001b Compare July 13, 2026 07:37
@wjllance
wjllance merged commit f2c909f into main Jul 13, 2026
7 checks passed
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.

[P0][Bug] unmatched order-response 缓冲永不衰减,长跑 live 必触发虚假 fail-closed

2 participants