Skip to content

Workers

Partial

Workers are how Omniglass means to do the steady background work (deriving samples, sending actions, firing timers, reconciling drift) on one machinery, with crash recovery and exactly-once outcomes for free everywhere. Not all of it rides a consumer today: the event rule sweep closes sustains and reconciles streaks on a server tick (ADR-0144 point 6), and calc rules drain a durable pending set on a tick of their own (ADR-0148). Both are replica-safe and lose nothing on restart; time as a primitive (#419) is where those loops converge.

The one machinery is a JetStream work-queue consumer over a configurable concurrency pool: pull, work, ack, at-least-once plus Nats-Msg-Id dedup and an idempotent sink (crash recovery, exactly-once outcomes, event-time semantics for free). That triple is the target contract, not the current one: today there is no Nats-Msg-Id dedup and the sink is not idempotent (#311, #430), and the built consumer dispatches serially on purpose, the state transition guard depending on it (internal/bus/consumer.go:246-253). It instantiates over several consumers rather than separate loops:

There is no separate projector: a current value is the latest series row, derived on read (ADR-0079; storage), and alarm / action hold their state directly.

Health is the exception, deliberately not a worker stage: its rollup (component -> system -> location) runs inside the write transaction that changed it, because a verdict recomputed a hop later would record its transition at the wrong moment. Parsing into samples is not a worker stage; it happens at the edge (collection).

The axis that decides almost everything else about a subsystem:

  • Stateless (owner resolution, calc): output is a pure function of (input, rules, snapshot). Order-free, safe to backtest, no cross-event state. Write pattern: append (a batched multi-row INSERT).
  • Stateful (the alarm lifecycle): maintains persisted state across events (the open alarm), so:
    • Order-sensitive. JetStream does not promise strict ordering (the server is ts-authoritative) and competing consumers can hand same-key messages to different members, so a stateful subsystem either tolerates reorder idempotently or serializes per state key. The alarm transition serializes per condition, today the raiser-supplied dedup_key (ADR-0075); the event_rule keying joins alongside when the rule engine lands, its ordered write in the same PG transaction as the event record.
    • Write pattern: guarded conditional upsert (INSERT ... ON CONFLICT / UPDATE ... WHERE), with a partial unique index as the concurrency-correctness backstop (built for alarms: alarm_open_condition_key, one open row per (component, dedup_key)).
    • Backtest is harder: it must process each entity’s series in order.