jonnifyredis

R3.01.1 · dive 1

List as wait + active

A queue is a list used one way: push at one end, pop at the other. A reliable queue is two such lists — and a job that moves between them.

Everything in a reliable queue rests on the Redis LIST. Push at the head with LPUSH, pop at the tail with RPOP, and the order out matches the order in — FIFO, with no extra structure. A reliable queue names two such lists, a waiting list and an in-flight list, and a job lives in exactly one of them; its position is its state. Push jobs into the waiting list on the right and pop them in FIFO order — the readout shows the contents, head, and tail.

LPUSH head, RPOP tail · FIFO over jobs 5, 6, 7

work (LIST) head · LPUSH tail · RPOP — — — — push 5, 6, 7 to the head; the tail holds the oldest

A list is a queue

A Redis LIST is an ordered sequence of strings with cheap push and pop at both ends. Treat one end as the head and the other as the tail, push at the head and pop at the tail, and the list becomes a first-in-first-out queue. LPUSH key value prepends to the head; RPOP key removes from the tail. A job pushed first is popped first.

LPUSH work 5      # head: [5]
LPUSH work 6      # head: [6, 5]
LPUSH work 7      # head: [7, 6, 5]
RPOP  work        # -> 5   (the oldest, popped from the tail)
RPOP  work        # -> 6

The list holds the order for free. The producer pushes at the head; the consumer pops at the tail; the list keeps the sequence between them. A queue is not a separate data structure — it is a list used with a push at one end and a pop at the other.

A queue is a list used one way: push at the head, pop at the tail, and the order out matches the order in — FIFO, with no extra structure.

wait and active are two named lists

A reliable queue does not use one list — it uses two. The first is the waiting list: jobs a producer has enqueued and no worker has yet taken. The second is the active (in-flight) list: jobs a worker has taken and is processing. A job lives in exactly one of them at a time, and its position names its state. A job in the waiting list is waiting; a job in the active list is being worked.

The two lists are named keys. EchoMQ names them with a queue prefix: emq:{queue}:wait is the waiting list and emq:{queue}:active is the in-flight list, where {queue} stands in for the queue name. The job does not change — it is the same string id — but which list holds it changes, and that change is the state transition. Reading a job's state is asking which list it is in.

emq:{queue}:wait    (LIST)   # jobs enqueued, not yet taken
emq:{queue}:active  (LIST)   # jobs taken by a worker, in flight
waiting list
The list a producer enqueues into — EchoMQ's emq:{queue}:wait. A job here is waiting for a worker.
in-flight list
The list a worker moves a job into for processing — EchoMQ's emq:{queue}:active. A job here is being worked, and a crash leaves it here, recoverable.
state = location
A job's state is which list holds it. The transition is the move between the two lists.

A reliable queue is two named lists, a waiting one and an in-flight one; a job lives in exactly one, and its location is its state.

The move between the two lists

Taking a job for work means moving it out of the waiting list and into the active list. Doing that as two separate commands — pop from wait, then push to active — opens a gap: between the two, the job is in neither list, and a crash there drops it. The next dive is exactly that gap and the single command that closes it. For now, hold the shape: two named lists, and a job that belongs to whichever one currently holds it.

A per-worker variant gives each worker its own in-flight list (processing:worker1, processing:worker2), so a recovery monitor can attribute a parked job to the worker that holds it. The lists are still LISTs, and the move is still a move between two of them — only the destination is now worker-specific. The third dive builds on that.

A job's state is its list · job 7 in wait, then in active

emq:{queue}:wait 7 emq:{queue}:active — job 7 is in wait · its state is waiting

The move that takes a job for work is a move between two lists; doing it in two commands opens a gap, and closing that gap is the next dive.

In EchoMQ — the two named lists

EchoMQ names the two lists in real code, in echo/apps/echomq. EchoMQ.Keys.wait/1 builds "#{base(ctx)}:wait" → emq:{queue}:wait, and EchoMQ.Keys.active/1 builds "#{base(ctx)}:active" → emq:{queue}:active. A new job is pushed to the waiting list; a worker that takes a job moves it into the active list. The move itself is moveToActive-11.lua's rcall("RPOPLPUSH", waitKey, activeKey) — the job leaves the wait LIST and lands in the active LIST as one command.

-- moveToActive-11.lua — the two lists, named (real)
local waitKey = KEYS[1]      -- emq:{queue}:wait   (EchoMQ.Keys.wait/1)
local activeKey = KEYS[2]    -- emq:{queue}:active (EchoMQ.Keys.active/1)
local jobId = rcall("RPOPLPUSH", waitKey, activeKey)  -- wait -> active

{queue} is a documentation placeholder for the queue name — the real builder is EchoMQ.Keys.base/1 ("#{prefix}:#{name}"), so the queue my_queue yields emq:my_queue:wait. It is not the cluster hash-tag {tag} that forces slot colocation; it is the queue name standing in for readability.

the pattern

A reliable queue is two named LISTs — a waiting list and an in-flight list — and a job whose state is which of them holds it.

in EchoMQ

EchoMQ.Keys.wait/1 builds emq:{queue}:wait and EchoMQ.Keys.active/1 builds emq:{queue}:active; the move between them is moveToActive-11.lua's RPOPLPUSH.

A reliable queue is two named lists and a move between them; EchoMQ's wait/1 and active/1 keys are exactly those two lists.

Door · not depth The full key layout — the prioritized set, the delayed set, the stalled set, and the event stream the worker path touches alongside wait and active — is the dedicated EchoMQ course. From here, this dive opens onto E2 · the engine — the as-built library and its full set of keys. This dive teaches the two-list primitive; that course teaches the whole layout.

References

The Redis list type and its two push/pop ends, and the queue layout EchoMQ ports.

Sources

Related in this course