Skip to content

Offload RSA-PSS signing to a thread executor — currently inline on the event loop #178

Description

@TexasCoding

From #106 polish backlog. Severity: low under normal load, medium under burst (batch order placement, dense WS reconnects).

Code

kalshi/auth.py:162 (sign_request) calls cryptography's RSAPrivateKey.sign(...) synchronously. RSA-PSS over a 2048-bit key is ~1–3 ms of pure CPU per signature on a modern machine; with a 4096-bit key it's closer to 10 ms.

Every authenticated REST request and every WS connect runs this on the asyncio event loop, blocking the loop for the duration. Under sustained high request rates (batch_create_v2, repeated WS reconnects after a network blip) this measurably stalls competing coroutines on the loop.

Fix

Wrap the sign(...) call in asyncio.to_thread(...) on the async path. The sync transport path stays inline — no event loop, no problem.

# In the async _base_client signing call site:
headers = await asyncio.to_thread(self._auth.sign_request, method, path, ts_ms)

Notes

  • cryptography's C-backed sign(...) releases the GIL, so executor offload is genuine parallelism for concurrent request bursts.
  • asyncio.to_thread uses the default executor (default min(32, cpu+4) threads since 3.8). Sufficient.
  • WS auth signs once per connect, so the gain there is bounded; the real win is REST burst paths.

Acceptance

  • sign_request is no longer called directly from async REST / async WS connect paths.
  • Microbench in tests/ (or a one-off script kept under scripts/): 1000 × sign_request under asyncio.gather does not block competing coroutines. Measure with a 100 Hz asyncio.sleep(0) ticker — pre-fix shows ms-scale schedule gaps clustered around sign calls, post-fix shows tight scheduling.
  • No regression in sync transport tests (tests/test_async_orders.py etc.).
  • tests/test_auth.py still green; the sync sign_request API is unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    polishCode-quality and DX improvements; non-functionalwsWebSocket-related

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions