Skip to content

WS perf: per-frame asyncio.Task + asyncio.shield allocation in WS recv loop #245

Closed

Description

@TexasCoding

Summary

Every WS frame (including orderbook_delta at potentially >1k msg/sec) allocates a Task, a Future, a contextvars copy, and a shield Future just to make _process_frame uninterruptible. On busy markets this dominates the recv-loop allocation budget and adds GC pressure that the #199 in-place work was meant to avoid.

Location

  • kalshi/ws/client.py:262-269 — per-frame create_task + shield

Evidence

inner = asyncio.create_task(self._process_frame(raw))
try:
    await asyncio.shield(inner)
except asyncio.CancelledError:
    ...
    try:
        await inner

Recommended fix

Drop the per-frame shield() and instead disable cancellation inside _process_frame via a single guard (e.g., process inline and use a _paused flag the pause path waits on, or shield once per pause-window rather than per frame). Verify with a bench against the current bench_ws_recv.py shape.

Severity & category

high / performance, ws

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