IU Core Event Worker — 03 Worker architecture
IU Core Event Worker — 03 Worker architecture
The problem
event_outbox is a shared append-only log (93k+ rows, mostly
system/alert). It has no delivery-state columns — no delivered_at,
no processed_at. It is not a work queue. A consumer therefore cannot mark
rows; it must track its own position.
Design
The worker is a SQL function, not a daemon — invoked, transaction-scoped,
atomic. No process, no cron, no container is created by this package
(invocation is a separate operator/DOT decision). The Python module
cutter_agent/iu_core/route_worker.py is a pure DB-free mirror of the
decision contract.
Substrate — migration 008
iu_route_worker_cursor— one row per named worker. Keyset high-water mark(last_created_at, last_event_id)over the iu-domainevent_outboxstream.event_domainscope, run counters,last_run_summary.iu_route_dead_letter— an iu event the worker could not route/deliver. Fullevent_snapshotso it is replayable without re-readingevent_outbox.idempotency_keyUNIQUE; resolved viaresolved_at+resolution, never DELETEd. The dead-letter table is the worker's retry queue.
iu_route_attempt (migration 002) is reused unchanged as the per-event audit
sink — it already has event_ref uuid, route_kind, status,
idempotency_key, UNIQUE (idempotency_key, attempt_no).
Worker function — migration 009
fn_iu_route_worker_run(p_worker_name, p_batch_limit) — one transactional pass:
- Dual fail-closed gate —
fn_iu_core_routes_enabled()(master) ANDfn_iu_route_worker_enabled()(dedicated worker kill-switch,dot_config 'iu_core.route_worker_enabled'). Either closed → clean no-op. - Claim — keyset batch
WHERE event_domain='iu' AND (created_at,id) > cursor ORDER BY created_at,id LIMIT n FOR UPDATE OF eo SKIP LOCKED.SKIP LOCKEDgives safe multi-worker concurrency. - Route — resolve
iu_outbound_routeby(domain,type,stream). - Decide + audit — per event, write exactly one
iu_route_attempt:dry_run(route enabled+dry_run — logs a decision, delivers nothing) /disabled/skipped(no route) /failed. - Dead-letter — a per-event
EXCEPTIONblock: one bad event cannot abort the batch; the failure is recorded as afailedattempt and snapshotted intoiu_route_dead_letter. - Advance cursor — atomically with the attempt writes (same txn).
Idempotency — no duplicate delivery
Deterministic key iu_wrk:{worker}:{route}:{event_id}. Two layers:
- Cursor — a re-run reads nothing already passed.
- Key — even if the cursor is reset,
ON CONFLICT (idempotency_key, attempt_no) DO NOTHINGwrites no duplicate attempt.
Delivery seam — fail-closed
fn_iu_route_deliver is the dry_run=false delivery point. It is
intentionally unbuilt — it RAISEs feature_not_supported. The worker
catches it and dead-letters the event. No downstream delivery is possible
from this package, by construction. Building the seam is the dry_run=false
macro (doc 08).
Recovery
fn_iu_route_dead_letter_replay(id) re-attempts one dead-lettered event as a
fresh iu_route_attempt (attempt_no incremented); resolves the row on a
dry_run/sent outcome.
Files
| File | Role |
|---|---|
sql/iu-core/008_route_worker_substrate.sql |
cursor + dead-letter tables |
sql/iu-core/009_route_worker_functions.sql |
5 functions + 3 views |
sql/iu-core/runtime/060_route_worker_activate.sql |
worker gate false + cursor seed |
sql/iu-core/runtime/070_route_worker_verification.sql |
read-only verify W1–W9 |
sql/iu-core/runtime/080_route_worker_disable.sql |
disable runbook |
cutter_agent/iu_core/route_worker.py |
pure decision-contract mirror |
Known limitation (for doc 08)
The keyset cursor advances monotonically. A row committed late with an earlier
created_at (a long-running unit_version txn) could be skipped. Acceptable for
a dry-run MVP; a dry_run=false delivery worker needs a safety-lag /
high-water-mark.