jonnifyechomq

E2.01 · Movement I · The Core

The lifecycle &
the state machine

A job is always in exactly one of eight states, and every move between them is one atomic Lua script.

A job in EchoMQ occupies one of eight states at a time — wait, paused, delayed, prioritized, active, completed, failed, waiting-children — and each is one Redis key built by one function in EchoMQ.Keys. Every move between states is one atomic Lua script: moveToActive-11 picks a job up and takes the lock, moveToFinished-14 completes it, moveToDelayed-8 retries it, and moveStalledJobsToWait-8 recovers a stalled one. When a script cannot complete it returns a negative integer from a closed -1…-11 set. The core is shipped, so every surface below is read from the real source. Three dives follow.

Step a job through the machine

The state machine is easiest to read by walking it. Step a captured job along two real paths and read the transition script named at each edge. The happy path is add → wait → active → completed; the retry path is add → wait → active → failed-handling → delayed → wait → active. Each move names exactly one Lua script, and the lifecycle never lets a job be half-moved — every transition is one atomic step.

A captured job's path · pick a path, then step

wait LIST active LIST · lock held completed ZSET · terminal delayed ZSET · score = run-at moveToActive-11 step 0 / 3

A job is always in exactly one state, and each move is one named atomic Lua script. The state and the script are the protocol — the same in Elixir, Go, and Node.js.

The dives

Three dives build the module — the states first, then the script behind each transition, then the lock that keeps a job owned.

How it fits

The lifecycle stands on the two immutable layers E1 froze — the L1 state keys and the L2 transition scripts — and sets them in motion. One transition makes the layering concrete: a worker picks up the next job, and at L4 it calls a runtime API; at L3 the executor dispatches a script by SHA (EchoMQ.Scripts.move_to_active/4); at L2 the script moveToActive-11 runs atomically; at L1 it moves a job id from emq:{queue}:wait to emq:{queue}:active and sets the lock at emq:{queue}:{jobId}:lock with a UUID token and a PX TTL. The completion script moveToFinished-14 first verifies that token; a missing or mismatched lock returns -2 or -6 from the closed set. L1 and L2 are byte-identical across runtimes; only L3 and L4 differ — the Golden Rule.

the protocol (immutable L1/L2)

The state keys (EchoMQ.Keys.wait/1, active/1, …) and the transition scripts (moveToActive-11, moveToFinished-14) — frozen, with the same closed -1…-11 codes in every runtime.

its three runtimes (variable L3/L4)

Elixir dispatches the moves with EchoMQ.Scripts over a Redix pool and renews locks with one EchoMQ.LockManager timer per worker; Go and Node.js drive the same scripts their own way. The wire underneath does not move.

The Golden Rule: L1 and L2 are immutable and shared; L3 and L4 vary. The eight states and the four transition scripts are the protocol — the same in every runtime; the executor that dispatches them is each runtime's own.

Start with the eight states, then read every transition and its script, then the lock protocol. The chapter closes with a workshop that runs a job through every state — reachable from E2 · the core.

References

Sources

Related in this course