Skip to content

SequenceTracker.track is async but only awaits on the rare gap path — every frame pays coroutine overhead #330

Closed

Description

@TexasCoding

Problem

SequenceTracker.track is declared async def solely so it can await self._on_gap(gap) in the gap and reset branches. The happy path (seq == expected, the case on every legitimate sequenced frame) does no I/O and no awaiting, yet _process_frame pays the full async-call overhead per frame: coroutine object allocation, __await__ step, frame teardown.

orderbook_delta and order_group_updates run at thousands of frames per second per subscription on a busy strategy. The previous perf wave (#245, #271) eliminated per-frame Task / shield / contextvars allocations exactly because they showed up in profiles; the per-frame track coroutine remains.

Evidence

  • kalshi/ws/sequence.py:49-118 — async signature, but the four early-return branches that fire on every frame do no awaiting
async def track(self, sid: int, seq: int | None, channel: str) -> bool:
    ...
    if seq == expected:
        self._last_seq[sid] = seq
        return True
    ...
    if self._on_gap is not None:
        await self._on_gap(gap)
  • kalshi/ws/client.py:417-419 — the recv-loop hot path awaits unconditionally
ok = await self._seq_tracker.track(
    sid, seq, msg_type if msg_type else channel
)

Suggested fix

Split into a sync track_sync(sid, seq, channel) -> tuple[bool, SequenceGap | None] that does the bookkeeping and returns the gap object (or None) without awaiting. The hot-path caller becomes:

ok, gap = self._seq_tracker.track_sync(sid, seq, channel)
if gap is not None and self._on_gap is not None:
    await self._on_gap(gap)

Keep the async track as a thin backwards-compat wrapper that calls track_sync then awaits the callback, for any external users and for the existing tests in tests/ws/test_dispatch.py / test_integration.py. Verify with scripts/bench_ws_recv.py.

Source

Round-3 independent audit (reviewer: performance).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance / hot-path concernwsWebSocket-related

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions