open atlas
↑ Back to track
System Design Foundations SD · 06 · 07

Async & messaging: code and config reading

Read real consumer code, broker config, and an outbox handler, then pick the answer a senior would commit to: an ack-order bug, a visibility-timeout duplicate, an inline-publish dual write, and an unbounded buffer.

SD Senior ◷ 14 min
Level
FoundationsJuniorMiddleSenior

Messaging bugs live in the code and config, not the prose: where you put the ack, what the visibility timeout is set to, whether the relay owns publishing, and whether the buffer has a bound. Read each snippet, trace the crash, and pick the answer a senior engineer would commit to.

Practise the loop you run during a design or code review: locate the load-bearing line, trace what a crash does to it, and choose the change that respects the delivery semantics instead of hiding them.

Snippet 1 — the ack order

def handle(msg):
    queue.ack(msg)          # acknowledged first
    charge_card(msg.amount) # then the side effect
Quiz

What delivery semantic does this give, and what's the risk for a payment handler?

Snippet 2 — the visibility timeout

# SQS consumer
visibility_timeout: 30      # seconds
# measured job processing time: p50 = 12s, p99 = 75s
# handler is NOT idempotent
Quiz

With these numbers, what happens to the slowest jobs, and what's the fix?

Snippet 3 — the outbox handler

with db.transaction() as tx:
    tx.insert(order)
    tx.insert(outbox_row(OrderPlaced))   # atomic with the order — good
    broker.publish(OrderPlaced)          # also published inline, "to save a hop"
    tx.commit()
# a separate relay ALSO reads the outbox and publishes
Quiz

The outbox row is written correctly, so why is the inline `broker.publish` a bug?

Snippet 4 — the buffer

const queue = []                       // in-memory, no max size
function enqueue(job) { queue.push(job) }   // never rejects
// worker drains at ~1k/s; producers can burst to ~8k/s during spikes
// symptom: during spikes, pod RSS climbs until it's OOM-killed
Quiz

Why does the pod crash under spikes, and what's the correct change?

Recall before you leave
  1. 01
    How does the ack's position decide the delivery semantic, and what's right for a payment?
  2. 02
    Why does the inline publish in an outbox handler reintroduce the dual write?
Recap

Every messaging decision in this unit shows up as a load-bearing line you can read straight off the code. The ack’s position sets the delivery semantic — ack-first is at-most-once (lossy), ack-last is at-least-once (duplicates), and a payment wants the latter plus idempotency. The visibility timeout must sit above the real p99 processing time and be heartbeated, or slow jobs get redelivered and run twice in parallel. The outbox only works if the handler does only the local transaction and a separate relay publishes — an inline publish silently reintroduces the dual write (phantom events plus relay duplicates). And an in-memory queue with no bound grows without limit under sustained overload until the process OOMs, so you bound it and fail fast (the backpressure signal), shedding when full and using a durable broker if buffered work must survive a crash. The senior habit is to find the load-bearing line, trace what a crash does to it, and pick the fix the failure mode demands — not the one that hides it.

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.