MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3423

(motir-core) Event FAN-OUT and the per-job cutover switch — one event to N subscribers, routed to either engine

Done
Description

Two mechanisms that ship together because neither is useful alone: how one event reaches many jobs, and how a single job is moved between engines without moving the rest.

Fan-out

sendEvent(name, data) writes one job_event row; a dispatcher enqueues one job_queue row per job whose trigger matches that name. work-item/transitioned therefore produces four runs, exactly as it does today.

Three properties that must hold, because their absence is silent:

  • A subscriber that throws does not prevent its siblings from being enqueued. Fan-out is not a transaction over the consumers.
  • Enqueue is idempotent per (event, job) — a dispatcher that retries must not double-enqueue.
  • The subscriber set is derived from the registry, not from a second hand-maintained list. Two lists drift.

The cutover switch

A per-job-id routing decision in sendEvent / defineJob: this job runs on the Postgres engine, that one still on Inngest. It is what turns a 24-job migration into 24 reversible one-line changes, and what lets the three cutover stories run in parallel.

  • The switch is configuration, not a code branch at each call site.
  • Its default is Inngest until a job is explicitly moved, so a job nobody has thought about cannot be silently migrated.
  • It is deleted by the retirement story once no second lane exists — it is scaffolding with a known end, and this card says so rather than leaving it to become permanent.

Acceptance criteria

  • One emitted event enqueues exactly one run per subscribing job — asserted against the real registry, so adding a subscriber cannot silently change the count without a test noticing.
  • A dispatcher retry does not double-enqueue: (event, job) is idempotent, tested by driving the retry.
  • A subscriber whose enqueue fails does not prevent the others from being enqueued.
  • A job routed to the Postgres engine runs there and does not also run on Inngest; a job not routed still runs on Inngest. Both directions tested — the second is the one that protects the 23 jobs this story does not move.
  • The switch's default is Inngest, and a job absent from the configuration takes that default.
  • Unit and real-Postgres integration tests ship with the change.

Context refs

  • lib/jobs/sendEvent.ts — the emit side
  • lib/jobs/registry.ts — the function list the subscriber set derives from
  • lib/jobs/defineJob.ts — where a job declares its trigger
  • lib/jobs/types.tsJobEventDataMap, the event-name to payload mapping