Skip to content

Public OrderbookManager.apply_snapshot copies yes/no dicts twice — once via inplace adopt, once via dict() overwrite #344

Description

@TexasCoding

Problem

The public apply_snapshot wrapper routes through _apply_snapshot_inplace, which adopts msg.msg.yes / msg.msg.no by identity into a fresh _BookState. Immediately after, the wrapper overwrites state.yes and state.no with dict(msg.msg.yes) / dict(msg.msg.no) to satisfy the #296 no-aliasing contract.

The identity adoption inside the inplace helper was therefore wasted work on the public path: the references are planted and then immediately replaced. Two allocations where one would do. The contract behaviour is correct; the perf shape is suboptimal.

Evidence

  • kalshi/ws/orderbook.py:130-156_apply_snapshot_inplace adopts by identity
# Ownership note: ``msg.msg.yes`` / ``.no`` are adopted by identity
# into the new ``_BookState``; callers must treat them as consumed.
...
state = _BookState(ticker=ticker, yes=yes_levels, no=no_levels, sid=sid_val)
  • kalshi/ws/orderbook.py:200-225 — public wrapper then overwrites with copies
self._apply_snapshot_inplace(msg, sid=sid)
ticker = msg.msg.market_ticker
state = self._books[ticker]
...
state.yes = dict(msg.msg.yes)
state.no = dict(msg.msg.no)
return state.to_orderbook()

Suggested fix

Parametrize _apply_snapshot_inplace with an adopt: bool flag — the recv-loop bypass passes adopt=True (current identity behaviour), and the public apply_snapshot passes adopt=False, in which case the helper itself does the dict(...) copy at construction time instead of adopting then overwriting. Document the chosen contract in the helper's docstring. Existing tests in tests/ws/test_orderbook.py covering #296's aliasing contract still cover the public guarantee.

Source

Round-3 independent audit (reviewer: performance+websocket).

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