There are over 25k lines of formated json send over sse events endpoint every second. Can we cut it down? Only send changes? Optimise it? #133

Closed
opened 2026-09-22 23:49:44 +02:00 by kamilkrzywda · 1 comment
Owner
No description provided.
Author
Owner

Fixed in v0.59.0 (PR #134, merge 1d9e9ade), deployed to https://flight.dev.kkhost.pl — verified /api/version = 0.59.0.

Measured first: the "25k lines" was devtools pretty-printing compact JSON; the real cost was ~597 KB per frame, pushed once per second (~583 KB/s per subscriber). 99.8% of it was packages: all 1,521 rows ever created — terminal packages (expired/delivered/ignored/abandoned) are never deleted from the DB while only ~12 were open at a time.

Fix: new pure sseFrame(snapshot) in lib/game/state.ts projects each frame to the package statuses any component actually renders (open, loaded, expired). Used by both SSE paths — the engine broadcast and the initial connect frame, so connecting never costs one full-history frame. Live payload: ~597 KB → ~10 KB per frame (≈60× cut), bounded forever by fleet/market size instead of growing with play time.

Deliberately unchanged: /api/state and action responses keep the FULL package set, so every e2e oracle asserting delivered/abandoned transitions via state lookups still works; the polling fallback stays a strict superset of what SSE pushes. No hash-skip (the snapshot's clock advances every tick — frames are never equal), no throttling (already 1 Hz), no diff/patch engine.

Tests: +3 unit tests in lib/game/state.test.ts; e2e/events.spec.ts now asserts no terminal status ever appears in a frame and frames stay < 200 KB.

Fixed in **v0.59.0** (PR #134, merge `1d9e9ade`), deployed to https://flight.dev.kkhost.pl — verified `/api/version` = 0.59.0. **Measured first:** the "25k lines" was devtools pretty-printing compact JSON; the real cost was **~597 KB per frame, pushed once per second** (~583 KB/s per subscriber). 99.8% of it was `packages`: all **1,521 rows ever created** — terminal packages (expired/delivered/ignored/abandoned) are never deleted from the DB while only ~12 were open at a time. **Fix:** new pure `sseFrame(snapshot)` in `lib/game/state.ts` projects each frame to the package statuses any component actually renders (`open`, `loaded`, `expired`). Used by both SSE paths — the engine broadcast and the initial connect frame, so connecting never costs one full-history frame. Live payload: **~597 KB → ~10 KB per frame (≈60× cut)**, bounded forever by fleet/market size instead of growing with play time. **Deliberately unchanged:** `/api/state` and action responses keep the FULL package set, so every e2e oracle asserting `delivered`/`abandoned` transitions via state lookups still works; the polling fallback stays a strict superset of what SSE pushes. No hash-skip (the snapshot's clock advances every tick — frames are never equal), no throttling (already 1 Hz), no diff/patch engine. **Tests:** +3 unit tests in `lib/game/state.test.ts`; `e2e/events.spec.ts` now asserts no terminal status ever appears in a frame and frames stay < 200 KB.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OpenCode/Flight-Ops#133