jonnify·redis /redis-patterns/flow-control

R6 · Flow Control & Scale

Staying stable under load.

A queue that runs and orders work has not yet learned to survive a flood. Flow control is the set of moves that keep the system stable under load — bound throughput to a budget, keep tenants fair, enqueue in bulk, and plan worker capacity — all without losing correctness.

The grounding is EchoMQ's real, shipped flow-control layer (echo/apps/echo_mq), backed by Valkey. The rate limiter spends down a counter key (emq:{q}:limiter) against a configured max over its own window, read with EchoMQ.Metrics.get_rate_limit_ttl/3; the fair-lane ring in EchoMQ.Lanes rotates one step per claim so every lane is served in turn; and EchoMQ.Jobs.enqueue_many/4 admits many jobs in one wire flush. Worked through codemojex's traffic. This chapter keeps the work of R3–R5 stable when demand exceeds what one moment can absorb.

§1

Why & when

echo/apps/echo_mq

A queue that distributes and orders work still trusts every caller to be well-behaved. Under load that trust breaks: one tenant floods the engine, a burst of writes outpaces the workers, a thousand small enqueues each pay a full round trip. Flow control is how the system stays correct and stable when demand exceeds what one moment can absorb. Each demand below has one matching technique, and the chapter is the set of those answers.

Throughput must stay under a budget

Cap how much work is admitted per unit of time — count what is admitted in the current window, refuse or defer once the count reaches the limit, and reset when the window rolls. The budget lives in the engine, not in any one worker.

Every tenant gets a fair turn

Under contention, no single tenant should starve the rest — a rota serves every lane in turn, and a weight tilts the share without ever letting a lane jump the line. Fairness is constructed, not hashed.

Bulk work must not pay per-item round trips

Admitting a thousand jobs one wire call at a time is a thousand round trips — one flush admits them all, in input order, under the same idempotency as a single add.

Worker capacity must be planned, not guessed

Each claim costs a fetch from the engine — concurrency is bounded by that per-claim cost and by the per-lane ceiling, so capacity is planned against the real fetch rather than assumed.

The flow-control lifecycle — enqueue → the limiter gate → the fair-lane ring → claim. Select a stage to see the real EchoMQ surface behind it; the gate spends a counter against max, the ring rotates one step per claim.
enqueue onto a lane limiter gate counter vs max · window fair-lane ring rota · one step per claim claim leased on the clock
readout · Select a stage to see the real EchoMQ surface behind it.

Every technique in this chapter answers one demand load makes on the queue — bound the throughput, keep tenants fair, enqueue in bulk, and plan capacity — each one a real move in EchoMQ's flow-control layer.

§2

The patterns

six modules · one built

One teaching module is built; the rest are specified. Rate limiting is the one new catalog pattern here; the others are flow-control techniques that compose primitives from earlier chapters. Each module is a hub with its dives, grounded in the real EchoMQ flow-control layer.

built ready to read soon specified
§3

How to apply

name the load problem · the technique follows

The hard part is matching the flow-control technique to the load problem you have. The four moves below are how a queue stays stable while many callers share it. Name the load problem, and the technique — and the real EchoMQ surface that becomes it — follows.

Name the load problem · get the flow-control technique and the EchoMQ surface that becomes it
readout · Select a load problem to see the technique and the real EchoMQ surface that answers it.

There is no single flow-control trick — only the technique that answers the load problem you have, each one a real move in EchoMQ's flow-control layer.

§4

The workshop

echo/apps/codemojex · EchoMQ.Lanes

The chapter closes with R6.06: rate-limit and fairly schedule codemojex's API and its jobs. codemojex is a Telegram emoji-guessing game on the same stack; a flood of guesses from one room must not starve the engine for everyone else. The budget is a per-queue counter — emq:{q}:limiter spent down to max over its window — global across every bot worker, because the counter lives in Valkey, not in any one process. Fairness rides the same fair-lane ring: each room is a lane the rota serves in turn, so a busy room takes its share and no more. Bulk admits ride EchoMQ.Jobs.enqueue_many/4, one wire flush for a burst of guesses.

Notes on Valkey. The {q} hashtag pins every key of one queue — emq:{q}:limiter, emq:{q}:meta, the ring, the lanes — to one of the 16384 cluster hash slots (CRC16 of the hashtag bytes, modulo 16384). One slot keeps a multi-key script legal — no CROSSSLOT — and co-locates the limiter counter every worker spends. valkey.io/topics/cluster-spec

§5

Up next

R7–R8

R6 keeps the queue stable under load. Each later chapter is a different concern over the same work — how the data lives in RAM, then running the tier in production.

R7soon

Data Modeling

How data lives in RAM.

R8soon

Production & Ops

Running the tier at scale.

§6

References

sources · related

Sources

Related in this course