Design canvas — pick the queue for the workload

Every queue decision — Standard vs FIFO, visibility timeout, prefetch, maxReceiveCount, ordering scope, fanout shape — collapses into one question per workload: what does failure cost, and what does latency cost?

Previously

You can read the meters and you can name every mechanism. Last move: stop being told the workload. Pick one, set every knob, and let the verifier challenge you — including the question 'should this have been Kafka?'

Scene 13

Design canvas — pick the queue for the workload

  1. Watch
  2. Try it
  3. Predict
  4. Capture
WORKLOADorder fulfillmentwelcome emailvideo transcodewebhook retryper-user inbox (leaderb…DESIGN CANVASQUEUE MODEStandardStandard for an unordered workload — best-effort o…VISIBILITY TIMEOUT30 stimeout 30 s is much larger than p99 2 s — real cr…PREFETCH10prefetch=10 sits in the 1–50 band CloudAMQP recomm…MAXRECEIVECOUNT5maxReceiveCount=5 sits in the 3–10 band; transient…ORDERING SCOPEnoneordering scope = "none" matches the workload's sma…FANOUTsingle queuesingle queue is the simplest fanout shape; correct…Would Kafka have been a better fit?NOwork-to-do, no history needed, single consumer pool — queue + at-least-once + idempotent …
What to watch for

Welcome-email workload is pre-loaded with sensible defaults — 30 s visibility timeout, prefetch=10, maxReceiveCount=5, Standard queue, single pool. Watch the verifier walk each slot in turn and name the earlier scene the default came from. Then switch workloads and watch a default explode.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

verify(workload, knobs)
runs the verifier pipeline; returns per-slot verdicts
def verify(workload, knobs):
verdicts = {}
# mq-09: visibility timeout vs p99 processing.
verdicts['visibility'] = check_visibility(workload, knobs)
# mq-09a: prefetch vs RTT / processing ratio.
verdicts['prefetch'] = check_prefetch(workload, knobs)
# mq-08: maxReceiveCount band + transient failures.
verdicts['dlq'] = check_max_receive(workload, knobs)
# mq-10: queue mode + ordering scope vs need.
verdicts['ordering'] = check_ordering(workload, knobs)
# mq-11: fanout shape vs consumer-group count.
verdicts['fanout'] = check_fanout(workload, knobs)
return verdicts
kafka_fit(workload)
the boundary decision tree — queue vs Kafka
def kafka_fit(workload):
# Acks delete (mq-02 / mq-05). No replay surface.
if workload.needs_replay:
return yes('queue acks delete — no history')
# Queue fanout copies the message into N queues (mq-11).
if workload.needs_multi_group_on_one_store:
return yes('one log, N readers — Kafka costume')
# Exactly-once across input+output topics.
if workload.needs_eos_input_to_output:
return yes('transactional producer + read_committed')
# Work-to-do, single pool, idempotent consumer suffices.
return no('queue + at-least-once + dedupe is honest')
Broker.process_cell(cell, worker_id)
the load-bearing event — every knob wired in
def process_cell(cell, worker_id):
# mq-09: hide from peers for visibility_timeout_sec.
cell.invisible_until = now() + visibility_timeout_sec
cell.receive_count += 1
# mq-08: too many receives → quarantine, don't redeliver.
if cell.receive_count > max_receive_count:
dead_letter_queue.send(cell); return
# mq-09a: lease counts against worker's prefetch budget.
worker.in_flight += 1 # capped at prefetch
try:
worker.handle(cell.body)
ack(cell) # mq-05: ack means DELETE
except TransientError:
nack(cell, requeue=True) # mq-06: back to the pool

Where this sits in Build a Message Queue (RabbitMQ / SQS)

Scene 13 of 14. Capstone: place a workload on the canvas, set every knob, flag whether Kafka would have been a better fit.

All 14 scenes in Build a Message Queue (RabbitMQ / SQS) · Every curriculum

Built with Arqly
Every scene in Build a Message Queue (RabbitMQ / SQS) builds on the one before it.All 14 Build a Message Queue (RabbitMQ / SQS) scenes