Appendix C - 4. Sole raw-Redis seam (the dispatch module)

The Structure of Sole raw-Redis seam (the dispatch module) — its shape at a glance:

Every caller reaches the store through the one dispatch module. That module declares the key schema and encodes atomic pop-and-move. A sole-seam lint bans the raw client anywhere else.

flowchart LR
  P[Producers] --> Seam
  C[Consumers] --> Seam
  M[Metrics] --> Seam
  Seam["Dispatch module<br/>(key schema + atomic ops)"] --> R[(Store)]
  P -. banned raw client .-> R
  L{{Sole-seam lint}} -. fails build .-> P

Accessible description: producers, consumers, and metrics all reach the store through the one dispatch module, which declares the key schema and encodes atomic pop-and-move. A dashed edge marks a caller using the raw client directly; the sole-seam lint fails the build on it.

Projected from the catalogue entry canonical-models-and-seams / Sole raw-Redis seam (the dispatch module).

On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns

Intent

Intent — Confine all raw-Redis access to one module, with every queue key declared there, so the queue's atomicity and schema invariants live in exactly one lint-enforced place.

Motivation

Raw Redis calls scattered across the codebase break two invariants at once. Atomicity: a pop-and-move done as ZPOPMIN + LPUSH (two commands) silently loses a job if the worker crashes between them. Schema: key names drift from the declared set, so a metric that reads queue depth misses a structure. Both recur wherever someone reaches for the raw client.

Applicability

Structure

The Structure diagram appears at the top of this page.

Sample Code

The seam's job is to make the non-atomic pop-and-move unwritable. A two-command sequence (pop from one structure, push to another) loses work on a crash between them; the seam runs the move as one atomic server-side script, so no caller can express the racy version. Every key the seam touches comes from one declared table.

KEYS = {"ready": "q:ready", "processing": "q:processing"}  # the one key schema

# atomic pop-and-move: pops the lowest-priority member and records it as processing
# in a single server-side step, so a crash can never lose the job between two commands.
_MOVE = """
local job = redis.call('ZPOPMIN', KEYS[1])
if job[1] then redis.call('HSET', KEYS[2], job[1], ARGV[1]) end
return job[1]
"""

class DispatchSeam:
    """The only module that speaks to the raw store. Callers get crash-safe verbs;
    they never hold the raw client, so they cannot write a two-command pop-and-move."""

    def __init__(self, client):
        self._client = client            # private — never exposed to callers
        self._move = client.register_script(_MOVE)

    def claim_next(self, worker_id: str) -> str | None:
        return self._move(keys=[KEYS["ready"], KEYS["processing"]], args=[worker_id])

Consequences

Example use within DocAble

Contents
© James C. Davis, 2026–present