Skip to content

0002 — Compute on read, no ticks ​

Date: 2026-11-09 · Status: accepted

Context ​

The check-in loop is the product: 3 minutes on a phone, nothing important requires being online at a specific moment (GAME-DESIGN.md §2, pillar 5, and §6). Most players are idle most of the time and the design must reach millions of them, so a million idle players must cost nothing — which rules out anything that advances every row on a schedule (ARCHITECTURE.md §1.2). The app is stateless and answers read pressure by adding processes (ARCHITECTURE.md §6).

Decision ​

State that changes with time is computed on read from a timestamp, never advanced by a background job (ARCHITECTURE.md §1.2):

  • Tax ledger = one row per colony per cycle, written by the daily tax sweep and read as a sum; nothing accrues between cycles.
  • Build queues = started_at + duration; completion derived, then lazily written when read.
  • Fleet position interpolated from departed_at, arrives_at, origin and destination.
  • Security and tax are arithmetic over a handful of stored integers (SECURITY-AND-SOVEREIGNTY.md §8).

One table, jobs, with a due_at index, is the entire scheduler. A worker claims rows with SELECT ... FOR UPDATE SKIP LOCKED and handles exactly three things: arrivals (resolve combat, apply loot, write reports), the daily tax sweep over claimed systems only, and lapse and decay (ARCHITECTURE.md §3).

Consequences ​

  • Nothing ever touches every row: no cron sweeps, no per-entity loops, no simulation loop.
  • The only background infrastructure is one small worker, horizontally scalable by adding more, with no coordination beyond SKIP LOCKED (ARCHITECTURE.md §3).
  • The two sweeps that scale with territory are bounded by design: the claimed set is limited by what the playerbase can afford to hold, "which is the only set that matters" (ARCHITECTURE.md §3).
  • Reads can write: queue completion and structure decay are lazily materialised on the first read after they are due.
  • Any new time-varying quantity must be arithmetic over stored timestamps and integers, or it does not fit.

Alternatives rejected ​

  • Per-entity tick loops. Cost scales with the number of entities rather than with activity, so idle players are not free — the opposite of ARCHITECTURE.md §1.2.
  • Cron sweeps over all rows. Explicitly excluded: "There is no cron that touches every row, ever" (ARCHITECTURE.md §1.2).
  • An in-memory simulation server. The app is stateless and scales by adding processes, and the scheduler already scales via SKIP LOCKED (ARCHITECTURE.md §3, §6). An in-memory authority holds the universe's live state in one process and must rebuild it on every restart. [INFERENCE from ARCHITECTURE.md §6]

Galaxy design documentation. Nothing here is implemented yet.