jonnifyredis

R4.03.1 · dive 1

Packing two keys in one score

A priority queue needs two orderings at once. EchoMQ packs both into one number — priority × 0x100000000 + counter — the tier in the high bits, the arrival in the low.

The whole pattern rests on one decision: what number goes in the score. A delayed queue puts the fire-time there; a priority queue puts a composite there — a number whose high thirty-two bits carry the priority tier and whose low thirty-two carry the arrival order. Move the sliders on the right to set a priority and a counter and watch the one score they pack, split into its two fields.

The composite score · tier × 0x100000000 + counter

2
7
composite score = priority × 0x100000000 + counter high 32 bits = tier × 0x100000000 — low 32 = counter — combined score —

The problem: two orderings, one score

A sorted set offers one ordering axis — the score. A priority queue needs two. Jobs in a higher-priority tier must come out before jobs in a lower tier, and within one tier the earliest-arriving job must come out first. A single number has to express both.

The move is positional. Reserve the high bits of the score for the tier and the low bits for the arrival order. Then the score compares tier-first automatically: any difference in the high bits dominates any difference in the low bits, because the high bits are worth more. Comparing two composite scores compares their tiers first and, only when the tiers are equal, their arrival counters.

A sorted set offers one ordering axis; a priority queue needs two, so the score reserves its high bits for the tier and its low bits for the arrival order, and the high bits dominate.

The formula

EchoMQ computes the score with getPriorityScore, an included Lua function whose canonical home is addPrioritizedJob-9.lua.

-- getPriorityScore.lua (included by addPrioritizedJob-9) — real
local function getPriorityScore(priority, priorityCounterKey)
  local prioCounter = rcall("INCR", priorityCounterKey)
  return priority * 0x100000000 + prioCounter % 0x100000000
end

0x100000000 is 232 — 4294967296. Multiplying the priority by it shifts the tier left thirty-two binary places, leaving the low thirty-two bits at zero. The arrival counter, taken % 232, then fills those low bits. The result is one number whose top half is the tier and whose bottom half is the position within the tier.

One IEEE double carries the whole thing exactly. A double holds fifty-three bits of integer precision; a small priority shifted into the high thirty-two bits plus a counter in the low thirty-two stays well under that ceiling, so the score is exact and the ordering never rounds.

The score is priority × 0x100000000 + (INCR pc): the tier shifted into the high thirty-two bits, the arrival counter in the low — one double carries both exactly.

The tier dominates the counter

Because the tier sits in the high bits, any change in the tier outweighs every value the counter can take. Two jobs in tier 1, however far apart their counters, both score below any job in tier 2 — the smallest tier-2 score is 2 × 232, larger than the largest tier-1 score, which is 1 × 232 + (232 − 1). The high bits set the band; the low bits position within it.

The hero figure above shows it directly: raise the priority slider by one and the score jumps by a full 0x100000000, dwarfing any counter move. The interactive below makes the comparison explicit, pitting two jobs against each other — one a step higher in tier, the other far ahead on the counter — and reporting which composite score is smaller and therefore pops first.

Tier beats counter · which job pops first

two composite scores compared — smaller pops first — — —

The tier sits in the high bits, so a change of tier outweighs any counter value; a tier-1 job at the top of its band still scores below the smallest tier-2 job.

A lower number wins

The formula makes a higher-precedence job a smaller number: priority 1 produces a smaller high-bit field than priority 2, so its composite score is smaller. The consume command is ZPOPMIN, which returns the smallest score first. The two compose into the convention a queue user sees: a lower priority number means higher precedence. Priority 1 beats priority 2 because 1 makes the smaller score and ZPOPMIN pops the smallest. The third dive walks that consume in real code.

A lower priority number makes a smaller score, and ZPOPMIN pops the smallest first, so a lower priority number is served sooner — the precedence is encoded as "smaller is sooner".

In EchoMQ — the composite score, in real code

The whole score is real code in echo/apps/echomq. The prioritized set is EchoMQ.Keys.prioritized/1 → emq:{queue}:prioritized. The arrival-counter key is EchoMQ.Keys.pc/1 → emq:{queue}:pc. The public entry EchoMQ.Scripts.add_prioritized_job/4 runs addPrioritizedJob-9.lua, passing Keys.pc(ctx) as KEYS[9]; the script calls getPriorityScore and then rcall("ZADD", prioritizedKey, score, jobId).

-- addPrioritizedJob-9.lua — score the job, then add it (real)
local score = getPriorityScore(priority, priorityCounterKey)   -- priority × 0x100000000 + INCR pc
rcall("ZADD", prioritizedKey, score, jobId)             -- place the job at its composite score

The same getPriorityScore is reused verbatim across the priority-aware scripts, so every path that scores a prioritized job packs the tier and counter the same way. The number chosen to carry the meaning is the whole pattern: the bare priority says only "this tier"; the composite says "this tier, and this position within it".

the pattern

A composite key packs two fields into one ordering key — the source packs them into the member and reads with ZRANGEBYLEX.

in EchoMQ

getPriorityScore packs them into the score: priority × 0x100000000 + (INCR pc) — the tier in the high thirty-two bits, the arrival counter in the low — so one numeric score orders by tier first and arrival second, read with one ZPOPMIN.

A composite key can pack into the member or the score; EchoMQ packs the tier and the arrival counter into one score, so the high bits set the tier and the low bits keep arrival order, in a single number.

Door · not depth The intra-group priority control plane — how a group prioritizes the jobs it owns, and how fairness is held across groups — is the subject of the dedicated EchoMQ course. The groups that prioritize within the set are E4 · Groups. This dive teaches the composite score; that course teaches the groups that use it.

References

The write that scores a prioritized job by its composite number, the sorted set behind it, the atomic counter folded into the low bits, and the queue protocol EchoMQ ports.

Sources

  • Redis — ZADD — add a member with its score; the write that scores a prioritized job by its composite number.
  • Redis — Sorted sets — score-then-member ordering, the axis the composite score occupies.
  • Redis — INCR — the atomic increment that produces the arrival counter folded into the low bits.
  • BullMQ — the queue protocol — the prioritized-jobs protocol EchoMQ ports, where the Lua scripts are the protocol.

Related in this course