R4.03.3 · Priority with composite scores — dive 3
Consuming with ZPOPMIN.
Pop the member with the smallest score: the highest-priority tier and, within it, the earliest arrival. One command returns the right job and removes it atomically.
Grounding: echo/apps/echo_mq/lib/echo_mq/lanes.ex · EchoMQ.Lanes.wclaim/3 — the real bus reaches the same head with a weighted ring rotation, the per-lane ZPOPMIN popping the mint-ordered front.
Static order — pops march down the tiers: B, D, F (tier 1 by arrival), then A, E (tier 2), then C (tier 3). Each pop removes the current smallest score.
The smallest score is the right job
lexicographic-sorted-sets.md.txtThe composite score is priority × 0x100000000 + counter. A smaller priority number makes a
smaller high-bit field; within a tier a smaller arrival counter makes a smaller low-bit field. So the smallest
score in the set is, by construction, the highest-priority tier and the earliest arrival within it —
exactly the job a priority queue should serve next.
ZPOPMIN removes and returns the member with the smallest score. The pop is atomic:
read-and-remove in one step, so two workers polling the same set never claim the same job. The job leaves the
set the instant it is chosen.
ZPOPMIN scored:jobs
# Returns: [ "job-A", "4294967297" ] — the smallest-score member, removed
The composite score makes the right next job the smallest number, and ZPOPMIN returns
the smallest atomically — one command serves the highest tier, earliest arrival, and removes it.
Why a lower priority number wins
lexicographic-sorted-sets.md.txt · ZPOPMINThe convention that surprises newcomers — priority 1 outranks priority 5 — is a direct
consequence of two facts composed. The formula makes a higher-precedence tier a smaller score:
1 × 232 is smaller than 5 × 232. And
ZPOPMIN always returns the smallest score first. Compose them and the lowest priority
number is served first. Nothing reverses or negates the value; the precedence is encoded as "smaller is sooner"
and read by "pop the smallest."
- the formula
- A higher-precedence tier is a smaller score:
1 × 232 < 5 × 232. - ZPOPMIN
- Always returns the smallest score first.
- composed
- The lowest priority number is served first — "smaller is sooner," read by "pop the smallest."
A lower priority number wins because the formula makes it the smaller score and
ZPOPMIN pops the smallest — precedence is "smaller is sooner," with nothing negated.
In EchoMQ: the same head, by a rotating ring
echo_mq/lanes.ex · wclaim/3EchoMQ serves the highest-precedence work next, but not by popping the smallest composite score from one set
— it has no per-job priority score to pop. Instead EchoMQ.Lanes.wclaim/3 runs a
weighted rotation: one LMOVE rotates a ring of serviceable lanes exactly once,
then serves that lane a fair share of K = min(weight, lane depth, glimit headroom) heads in one
atomic turn. The per-lane ZPOPMIN still does the pop — but it pops the head of the
rotated lane, which the score-0 set keeps in mint order, so within a lane it is FIFO.
A higher-weight lane is served proportionally more over a window, never all of it — so precedence is
the lane's weight and fairness is constructed by the rotation, not read from a packed number. The consumer
codemojex rides this directly: Codemojex.NotificationWorker drains the
cm.notify lanes, one fair lane per chat, so one chat's burst cannot starve others.
-- @gwclaim (lanes.ex) — rotate the ring once, then serve K heads of that lane, trimmed local g = redis.call('LMOVE', KEYS[1], KEYS[1], 'LEFT', 'RIGHT') -- rotate the ring of serviceable lanes local lane = ARGV[1] .. 'g:' .. g .. ':pending' local w = tonumber(redis.call('HGET', ARGV[1] .. 'gweight', g) or '1') -- the lane's throughput share local k = w if depth < k then k = depth end -- never over-pop the lane -- for _ = 1, k do local popped = redis.call('ZPOPMIN', lane) ... end
Notes on ValkeyZPOPMIN removes and returns the member with
the lowest score, atomically — the per-lane pop EchoMQ runs inside the weighted rotation —
valkey.io/commands/zpopmin.
One pop, but across a ring of lanes
The textbook serves the next job with one ZPOPMIN over a composite score. EchoMQ keeps the
per-lane ZPOPMIN for within-lane FIFO, but reaches across tiers with the weighted ring
rotation in wclaim/3: precedence is the lane's gweight. The lane ring, the rotation,
and the order theorem are the Queue pillar of the EchoMQ course.
One ZPOPMIN over a composite score returns the next job — the score already orders by
tier then arrival, so the smallest is always the right one.
EchoMQ keeps the per-lane ZPOPMIN for within-lane FIFO but reaches across tiers with a
weighted ring rotation in wclaim/3: precedence is the lane's gweight, the head is
the lane's mint-ordered front.
The pattern pops one smallest composite score; EchoMQ pops each lane's mint-ordered head and reaches across tiers by rotating a ring weighted per lane — same goal, the precedence on the lane.
Recap — one pop, the right job
back → R4.03The composite score makes the next job the smallest number, and one ZPOPMIN serves it: highest
tier, earliest arrival, removed atomically — a lower priority number wins because it is the smaller score.
EchoMQ reaches the same head differently: a weighted ring rotation in wclaim/3 picks the lane by
its gweight and pops its mint-ordered front. Return to the module hub for the whole arc, or open
the Queue pillar of the EchoMQ course for the rotation in depth.
References
sources · relatedSources
- Valkey — ZPOPMIN — pop the member with the smallest score, atomically; the consume EchoMQ runs per lane on the engine it ships on.
- Redis — ZPOPMIN — the same command's Redis reference; pop the smallest-score member.
- Redis — Sorted sets — score-then-member ordering, the basis for "smallest score is the right job."
- Redis — ZADD — the add side
ZPOPMINreads back; the score that ranks the job (score 0 in EchoMQ's lane sets).
Related in this course
- R4.03 · Priority with composite scoresthe module hub
- R4.03.1 · Packing two keys in one scorethe composite score this pop reads back
- R4.03.2 · FIFO within a tierthe counter, and EchoMQ's score-0 mint order
- R4.01.2 · ZRANGEBYSCORE promotionthe sibling consume
- R4 · Time, Delay & Prioritythe chapter
- /echomq · Queuethe lane ring, weighted rotation, the order theorem