Repository navigation
fix(ws): orderbook resync; seq reset detection; permanent close codes; error context; clean unsubscribe - #231
Conversation
Code Review — PR #231 (Wave 1-C WS Correctness)This is a large, well-scoped correctness batch. The overall direction is right and the issues addressed are real. Notes below, roughly ordered by severity. Correctness issues
The fallback for unmodeled channels (
prev_stashing = self._stashing
self._stashing = True
try:
...
finally:
self._stashing = prev_stashingIf Duplicate The same
Both Potential bugs
KalshiSequenceGapError(
...
last_seq=gap.expected - 1,
next_seq=gap.received,
)For a forward gap,
old_bucket.discard(ticker)
if not old_bucket:
self._sid_tickers.pop(prior.sid, None)
except KalshiSubscriptionError:
logger.debug(...)
self._sid_to_client.pop(old_sid, None)
sub.server_sid = NoneThe Design observations
This is a duck-typed workaround for the websockets library's internal structure. It works for the current websockets version, but is fragile if websockets changes its exception attributes. A small comment linking to the websockets version this was tested against would help future maintainers. Alternatively, catch
The function handles this correctly (
Would make the type checker catch callers that misspell the string. Minor. Test coverageThe test suite is thorough for the happy path and key error paths. A few gaps:
Style / conventions
SummaryThe critical fix (#189 — real resubscribe instead of clear-and-pray) is well-implemented and the per-sid ticker tracking in Blocking items: none. |
…; error context; clean unsubscribe - _handle_seq_gap drives a real unsubscribe+resubscribe for orderbook_delta AND order_group_updates (was orderbook-only, was just clear-and-pray). All-markets subs supported via per-sid ticker tracking in OrderbookManager (apply_snapshot records sid; remove_by_sid/tickers_for_sid expose teardown). - SequenceTracker: three-way distinction (seq==last drops, seq<last fires reset gap, forward gap unchanged). SequenceGap carries kind='gap'|'reset'. - WS recv loop fast-fails on permanent close codes (RFC 6455 1002/3/7-10 + 4xxx range) instead of burning the 10-retry budget. - Subscription.to_subscribe_params validates params keys against per-channel allow-set; raises KalshiSubscriptionError on unknown key. Generic subscribe escape hatch falls back to the union of forwarded keys for unmodeled channels. - KalshiWebSocket.unsubscribe() tears down OrderbookManager state for the sub via remove_by_sid. Server-initiated unsubscribe (dispatch._handle_server_unsubscribe) does the same via the new orderbook_mgr handle on MessageDispatcher. - ERROR backpressure strategy: MessageQueue.put_error surfaces the exception through __anext__. _process_frame routes BackpressureError to the affected sub's queue before re-raising. put_sentinel is idempotent after close so the recv-loop fan-out doesn't evict a real frame from the deque. - Every raise site in kalshi/ws/ populates the new channel/sid/last_seq/next_seq/op kwargs added in W0-B. SubscriptionManager.resubscribe_one + broadcast_error wire the resync flow. Closes #189, Closes #195, Closes #196, Closes #197, Closes #205, Closes #206, Closes #207, Closes #213 Refs #224
60fb0fd to
f5db1a1
Compare
Code Review — Wave 1-C WS Correctness SweepThis is a well-scoped, well-motivated batch fix. The critical issues (#189, #196, #207) were genuine correctness bugs — silent state corruption, silent error swallowing — and the solutions are sound. Overall: approve with minor notes. What the PR doesFixes 8 WebSocket correctness issues in one coordinated batch:
Code QualityStrengths:
Minor issues:
TestsTest coverage is thorough and well-structured. A few observations:
Security / Correctness
SummaryThe critical correctness fixes are solid. The main item worth a follow-up is the stash-drain question in |
Wave 1-C of the audit umbrella. 8 issues; the largest WS-correctness batch including the critical orderbook resync.
Changes
_handle_seq_gapdrives a real unsubscribe+resubscribe for orderbook_delta AND order_group_updates (was orderbook-only, was just clear-and-pray). All-markets subs supported via per-sid ticker tracking inOrderbookManager. Local book repopulates after a gap instead of staying permanently empty.Subscription.to_subscribe_paramsvalidates param keys against a per-channel allow-set; raisesKalshiSubscriptionErroron unknown keys (typo-trap fix).SequenceTracker.trackdistinguishes seq==last (drop), seq<last (reset → gap-recovery), out-of-order (drop). Was silently dispatching reset messages.KalshiConnectionErrorinstead of burning the 10-retry budget on doomed reconnects.order_group_updatesgap routed through the same resubscribe helper as orderbook (was: no recovery at all).KalshiWebSocket.unsubscribe()tears downOrderbookManagerstate for the sub's tickers (was: long-running sub/unsub cycles leaked orderbook entries forever).KalshiBackpressureErrorthrough the consumer iterator (was: silentStopAsyncIteration, indistinguishable from clean close).KalshiSequenceGapError/KalshiSubscriptionErrorraise site inkalshi/ws/populates the newchannel/sid/last_seq/next_seq/opkwargs added in W0-B.Tests
27 new tests. ruff + targeted pytest green.
Coordination note
Coordinated with W1-D (
audit/w1d-ws-payloads) via IRC during execution. Clean merge — no orderbook.py conflict.Closes #189, Closes #195, Closes #196, Closes #197, Closes #205, Closes #206, Closes #207, Closes #213
Refs #224