Skip to content

The life of a message

Keyboard: ← → to move, F for full screen, O for overview.

Tan focused on the work

Muhammet Şafak — Presentations

The life of a message

Schema, delivery, repeat, replay: a message is tested four times in the queue.

Muhammet Şafak

Tan pointing

Agenda

Four stops

  1. Schemavalidate at the edge
  2. Evolutionwithout breaking the contract
  3. Idempotencythe same message twice
  4. DLQ replayreset, select, test

Section 01

Schema

The queue doesn't look inside the payload; you add the shape guarantee at the edge.

The queue carries bytes, not types

Where is the schema validated?

An upstream service dropped a field from the payload; the worker threw a KeyError in production.

  1. Producer sideprimary defense
  2. Consumer sidesafety net → DLQ
  3. Schema registryper message type

The consumer side is the seatbelt, not the steering.

Muhammet ŞafakValidating the schema at the edge of the queue · sade.dev

Loosening is safe, tightening is not

Changing a schema without breaking it

Safe

  • Add an optional field
  • Drop the required constraint
  • Widen an enum, relax a minimum

Breaking

  • Add a required field
  • Remove, rename or retype a field
  • Narrow an enum, tighten a rule

Consumers upgrade first

A new identity, parallel lives

  1. Step 1: Publish v2orders:created.v2
  2. Step 2: v1 continuesproducers emit both
  3. Step 3: Consumer migrationon their own schedule
  4. Step 4: Retire v1once it has no consumers

Section 02

Repeat

The broker keeps its promise: a message arrives at least once, sometimes twice.

The same message, two charges

The same message can arrive twice. The charge must not happen twice.

The payment worker charged the card and crashed before it could ack the message; the broker redelivered it, and the customer was charged twice.

f(f(x)) = f(x)

Idempotent or not?

Not idempotentIdempotent
ExampleIncrease the balance by 10Set the order status to paid
TwiceAdds 20, or two emailsSame as the effect applied once
NeedsA key + dedupePossibly nothing

The side effect decides

When do you record the key?

Concurrent duplicates need a constraint, not a check: a unique constraint on the key column turns the second insert into a conflict.
ShapeWhen recordedWindowFits
Seen-setAfter the handler returns successfullyYesThird-party calls: email, charge
TransactionalIn the same DB transaction as the changeNoneA row you own

Keeping the key honest

Key and constraint

  • SenderThe sender produces the key: the message id or the Idempotency-Key header.
  • PayloadDon't derive it from the payload alone: two legitimately identical requests collide.
  • ConstraintA constraint, not a check: a unique constraint turns a concurrent second insert into a conflict.

Section 03

Replay

Replay isn't moving bytes back: it's resetting a message and safely reprocessing it.

20

Example scenario

minute outage

The downstream API was down for twenty minutes; four thousand messages used up their retries and landed in the DLQ.

A naive replay poisons again and bills twice

Replaying a DLQ safely

  1. Step 1: Resetid, payload, trace_id stay
  2. Step 2: Select the setreason: timeout
  3. Step 3: Dry-runcount without touching
  4. Step 4: Sandboxside effects stubbed
  5. Step 5: Productionif the first two are boring
Tan waving

Thank you

sade.dev

Validate the schema at the edge, suppress repeats, replay by testing first.

Share, embed, download