Skip to content

feat: single monotonic order-book version chained across snapshots and deltas#71

Merged
emrebulutlar merged 1 commit into
mainfrom
feat/orderbook-versioning
Jul 5, 2026
Merged

feat: single monotonic order-book version chained across snapshots and deltas#71
emrebulutlar merged 1 commit into
mainfrom
feat/orderbook-versioning

Conversation

@emrebulutlar

Copy link
Copy Markdown
Member

Adds the ordering primitive the market-data book stream was missing: ONE version per book (not per side), stamped from the Aeron log position, chaining snapshots and deltas so any client can join mid-stream, stitch deterministically, and converge on the identical book.

Design

  • Engine: each mutating command stamps bookVersion = max(current+1, Header.position()) before processing — deterministic across replicas, never regresses across restarts/failovers, and needs no engine-snapshot format change (the first post-restore command jumps to the live log position). Published through the existing seqlock (array engine) / snapshotLock (direct engine) so the flush thread's (arrays, version) pair is consistent.
  • SBE schema v4 with sinceVersion=4 extension fields (mixed-version safe during the rolling update): BookSnapshot.bookVersion, BookDelta.bookVersion + fromVersion. Deltas naming the state they extend makes gaps DETECTABLE despite flush-level coalescing.
  • Gateway: null-guarded decode (pre-v4 upstream ⇒ legacy per-side max), chain tracked in GatewayOrderBook (JSON bookVersion), continuity validated (warn + counter; session-open resnapshot re-establishes after seams), chain fields forwarded on broadcast.

Client protocol (trading-ui#35)

Subscribe to deltas first → buffer → snapshot(V) arrives → drop buffered deltas ≤ V → replay the rest, verifying each fromVersion continues the chain → any gap ⇒ automatic re-snapshot. All clients see the same book and the same updates.

Verification

  • 230 match tests green; full package green.
  • Gateway deployed to dev: bookVersion live in REST/WS JSON (legacy fallback until the cluster rolls).
  • Cluster rolling update pending operator approval (deliberately not auto-run).

🤖 Generated with Claude Code

…d deltas

Adds the market-data ordering primitive the book stream was missing:
one version per BOOK (not per side), stamped from the Aeron log
position, chaining snapshots and deltas so any client can join
mid-stream and converge on the identical book.

- Engine: each mutating command stamps the market's bookVersion as
  max(current+1, Header.position()) BEFORE processing — deterministic
  across replicas (same log) and never regressing across restarts or
  failovers without touching the engine snapshot format. Both engine
  impls publish it through their existing seqlock/snapshotLock so the
  flush thread's collected (arrays, version) pair is consistent.
- SBE schema v4 (extension fields, mixed-version safe during a rolling
  update): BookSnapshot gains bookVersion; BookDelta gains bookVersion
  + fromVersion — every delta names the published state it extends, so
  consumers can DETECT gaps despite flush-level coalescing.
- MarketPublisher maintains the published chain
  (fromVersion = last published state's version).
- Gateway: decodes with null-guards for pre-v4 upstreams, tracks the
  chain in GatewayOrderBook (JSON 'bookVersion', with 'version' now the
  same value), validates continuity (counter + warn; the session-open
  resnapshot re-establishes after seams), and forwards
  bookVersion/fromVersion on broadcast deltas.

Client protocol this enables (implemented in trading-ui): subscribe to
deltas first, buffer; fetch a snapshot (version V); drop buffered
deltas with bookVersion <= V; replay the rest verifying each delta's
fromVersion continues the chain; on any gap, re-snapshot. All clients
see the same book and the same updates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@emrebulutlar
emrebulutlar merged commit 80197a5 into main Jul 5, 2026
2 checks passed
@emrebulutlar
emrebulutlar deleted the feat/orderbook-versioning branch July 5, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant