jonnifyechomq

E3 · Movement II · The Extension

EchoMQ 2.0 —
the protocol break

One deliberate, versioned fork: the wire becomes EchoMQ's own, every key declared, every queue placed.

Movement I taught the as-built core — the v1 line, BullMQ-wire-compatible, frozen at 1.3.0. Movement II opens with the fork: emq.1 ships EchoMQ 2.0 — the emq:{q}:… keyspace replacing emq: with the per-queue hashtag applied transparently by the core, the reserved {emq}: base for the core's own keys, every Lua key declared in KEYS[], meta.version echomq:2.0.0 with a two-way boot fence, and an explicit v1→v2 migration path. BullMQ compatibility is dropped by design. The rung is drafted — nothing of v2 exists on disk yet, so this chapter teaches from the emq.1 spec ("emq.1 ships <surface>") and is re-grounded when the rung lands.

Why & when

The v1 wire carries two structural inheritances that cap it on any placement-aware store — wherever a key's {…} hashtag decides its placement. First, the undeclared-keys flaw: roughly half of the 50 inherited Lua scripts (about 24–26, pattern-dependent) build per-job keys inside the script (local jobKey = keyPrefix .. jobId — moveToActive-11.lua:148) and never declare them in KEYS[] — outside the engine scripting contract (Valkey's own guidance: every key a script accesses arrives as an input key argument). Second, the unplaced keyspace: EchoMQ.Keys interpolates the queue name verbatim — no hashtags, so nothing for the threads to balance on. Containing both under compatibility has a proven ceiling; resolving both means breaking the wire. So the wire breaks — once, versioned, at the first rung.

Read this chapter first if you follow the build into groups (E4), batches (E5), lifecycle controls (E6), or the cache (E7) — each rides the v2 protocol this break establishes. The interactive below shows the fork directly: the same queue's keys on the v1 line and under the 2.0 protocol emq.1 ships.

The fork · queue payments, two protocol lines

one queue · two protocol lines · the fork is versioned the v1 line — frozen at 1.3.0 emq:payments:wait · active — LIST (no hashtag) emq:payments:{jobId}:lock — concat inside Lua ~24–26 of 50 scripts: undeclared keys meta.version = bullmq:5.65.1 undeclared keys — outside the engine scripting contract EchoMQ 2.0 (emq.1 ships) emq:{payments}:wait · active — placed, declared {emq}: — the reserved core namespace every key in KEYS[] — no escape hatch meta.version = echomq:2.0.0 + the fence one queue ⇒ one slot · queues spread

The triangle this chapter stands on

the pattern · and the evidence

R0 · foundations (load-once, SHA-dispatched scripts) and R2 · atomicity (a multi-key change as one indivisible Lua move) — plus the DF undeclared-keys audit, the measured flaw the break resolves.

the spec · ⇄ emq.1 · drafted — the break

The rung tracked, specs/emq/emq.1.md (rewritten 2026-06-10): the emq:{q}:… keyspace, the {emq}: reserve, the declared-keys script set, echomq:2.0.0 + the fence, the migration path. Taught from the spec until the rung ships.

the as-built code — the v1 evidence

Present tense: EchoMQ.Keys interpolates verbatim (no hashtag); moveToActive-11.lua:148,162 concatenates undeclared per-job keys; EchoMQ.Scripts.execute_raw/4 sends only the declared list; meta.version = bullmq:5.65.1, the line v2 freezes at 1.3.0.

Start here — the orientation

Three orientation dives take the break's big ideas in turn, before the granular modules: the keyspace EchoMQ owns once it stops speaking emq:, the versioned fence and migration path that make one break safe, and the conformance probes that replace compatibility as the standing check.

The full chapter

The orientation above is the front door; the depth is four modules and a workshop, each a hub with three dives, re-derived from the rewritten rung's deliverables — as each is authored, its card here links through.

built ready to read soon specified
E3.01soon

The owned keyspace

emq.1 ships emq:{q}:… with the hashtag applied transparently by the core, and reserves {emq}: for the core itself. Dives: the emq prefix · the transparent hashtag · the {emq}: reserve.

E3.02soon

The declared-keys script set

Every Lua key in KEYS[] — the inherited undeclared-keys flaw resolved at the root, so the engine sees each script's true key set before it runs. Dives: the flaw · the declaration discipline · the placement payoff.

E3.03soon

The versioned fence & branded ids

meta.version echomq:2.0.0 with a two-way boot fence for mixed fleets; the integer Snowflake branded at the edge. Dives: the version fence · the snowflake · the codec-locality rule.

E3.04soon

The two probes — conformance & fleet interop

The v2 conformance capture and the fleet round-trip that prove the owned wire every rung. Dives: the conformance probe · the fleet interop · the diff rule.

E3.05soon

Workshop

Walk the v1→v2 migration on a real queue and run both probes green on the v2 wire — the conformance diff empty, the fleet round-trip closing.

How it works

The break is one rung with one mechanism: own the keyspace, declare the keys, version the wire. emq.1 ships the key builders that write emq:{q}:wait, emq:{q}:active, emq:{q}:<jobId> — the per-queue hashtag inserted by the core, never by the caller — and a script set in which every key a script touches is listed in KEYS[] before the script runs. The v1 line concatenates per-job keys inside the script (local jobKey = keyPrefix .. jobId, moveToActive-11.lua:148), outside the engine scripting contract — Valkey's guidance: every key a script accesses arrives in KEYS[]. With every key declared and placed by its {q} hashtag, each call's key set is slot-local and known before it runs. meta.version records echomq:2.0.0; a two-way boot fence refuses cross-version contact; the migration path moves a v1 deployment over once, deliberately.

the pattern · ← redis-patterns R0, R2

A multi-key change is one indivisible Lua move over KEYS/ARGV with no foreign command (R2), dispatched once by SHA (R0) — and the KEYS[] declaration is load-bearing: it is how the store knows what a script will touch before it runs.

the implementation · ⇄ emq.1 (drafted)

emq.1 ships the emq:{q}:… keyspace and the declared-keys script set, so every move keeps the v1 line's atomicity and gains a true, declared key set — slot-local by construction, no escape hatch.

The break is the one rung that ships no feature — it ships the wire, so every later rung is a set of declared-keys scripts on an owned, placed keyspace, and "correct on the v2 wire" is a check that runs.

The chapter closes with E3.05 · the workshop — walk the v1→v2 migration on a real queue and run both probes green on the v2 wire, the conformance diff empty and the fleet round-trip closing.

The 2.0 fork — EchoMQ leaves the BullMQ wire

This chapter is where the fork happens. The v1 line (frozen at 1.3.0) speaks emq: and inherits the undeclared-keys flaw; emq.1 ships the whole break in one rung — the owned emq:{q}:… keyspace, the {emq}: reserve, every Lua key declared, echomq:2.0.0 behind a two-way fence, and the migration path — so EchoMQ runs on an owned, placed keyspace of its own. Broken once, versioned, then owned.

Redis Patterns Applied

This chapter is the depth on the far side of two redis-patterns doors. The foundations it stands on — load-once, SHA-dispatched scripts and the keyspace-as-contract discipline — are taught one excerpt at a time in R0 · Overview; the atomic multi-key move the break preserves is taught in R2 · Coordination. There a reader meets each pattern once and portably; here those patterns become a protocol of EchoMQ's own — every key declared, every key placed — verified against Valkey, the conformance truth.

Up next — the rest of Movement II

E4–E8 each build on the 2.0 wire emq.1 ships — declared-keys scripts on the owned, placed keyspace. They are specified and on the way; each teaches from its rung's spec until the rung ships, then is re-grounded in the real modules.

Movement II tracks the EMQ extension ladder rung by rung. The ladder is drafted, so each chapter teaches from its rung's spec; building course pages does not ship the rung.

References

Sources

Related in this course