Skip to content

Build1 publisher2 min readPublished

A DynamoDB outbox pushes the deduplication work into the consumer's transaction

miruky's console walkthrough puts an order and its outbox event in one DynamoDB transaction, then fails the consumer on purpose to show why redelivery is the consumer's problem. The whole exercise is priced under $5 in us-east-1.

The Engineer · Build desk

Photograph accompanying A DynamoDB outbox pushes the deduplication work into the consumer's transaction
Photo: amazon.science

What happened

  • miruky built the whole path in the AWS Console, with no terminal required, and deliberately failed the consumer after its transaction to see what a retry does.
  • The relay cannot atomically send to SQS and advance its stream checkpoint, so the consumer has to tolerate repeated delivery of the same event.
  • Deduplication keys on the application-level event_id, and a payload digest rejects conflicting content under that identity, independently of transport IDs.
  • The walkthrough prices a deliberately oversized one-day allowance at about $0.58 before free-tier benefits, against a stated target of under $5 in us-east-1.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Correctness lands on the consumer side. Every business effect has to sit in the same transaction as its inbox marker, or a redelivered event adds to the sales summary twice.
  • decision Anyone reproducing this has to build the failure path first: the dead-letter queue must exist before the delivery queue that references it.
  • cost At under $5 the dedup argument can be settled on an engineer's own card before it reaches a design review, and the person paying is the person testing.
  • capability A digest over the payload gives the consumer a way to catch two different bodies claiming one event_id, which a transport-level message ID cannot see.

A database commit can succeed while the caller believes processing failed. Retrying then has to answer whether the business effect already happened. miruky's command transaction writes the order and its outbox event together in one table, with key prefixes separating business state from outgoing event intent, and no secondary indexes on it. The consumer's transaction writes an inbox marker and updates the sales summary in the same step.

"An outbox preserves event intent; it does not provide exactly-once transport," miruky wrote.

miruky feeds the relay from the table's own change stream, with New image selected, because a key-only record would not carry the inserted outbox body the relay has to forward. Inbox markers and the sales summary live in a second table, which has no stream.

Queue settings are where the retry budget gets fixed. Visibility timeout is 60 seconds against a 10-second consumer, six times the runtime, which the walkthrough attributes to an AWS recommendation. Maximum receives is 5. So the dedup path has to absorb up to four redeliveries of the same event_id before the message goes to the dead-letter queue. Dead-letter retention is 4 days against the delivery queue's 1 day, four times the window for looking at what failed.

The $0.58 estimate covers a deliberately oversized day: 10,000 Lambda invocations at 256 MB and 10 billed seconds, 100,000 SQS request units, 100,000 DynamoDB write units, 50,000 read units, and 0.1 GB of ingested logs. Divided into the under-$5 target, that is roughly eight such days. miruky notes that transactional reads and writes consume additional units, and that the thing to count is billing units, not business events. For the figure to transfer, you need us-east-1 and the Lambda, SQS, DynamoDB and CloudWatch rates as checked on September 5, 2026, and you need the setup to stay free of a NAT gateway, a customer-managed KMS key, provisioned polling, a public endpoint and a continuous producer. miruky says the $5 is not a spending cap and calls the final billed amount unverified.

Three IAM roles carry inline policies scoped to the table, stream, delivery-queue and log-group ARNs, each log resource ending in :* to reach its streams, instead of broad managed policies. Resource names in the post are random strings, so keep a mapping if you substitute your own. Each function logs to its own group, Standard class, 1-day retention, no customer-managed encryption key.

What to watch

  • AWS rates for Lambda, SQS, DynamoDB and CloudWatch were checked on September 5, 2026; a rate change moves the $0.58 day.
  • Whether the final bill lands under $5, which miruky says is unverified and not a spending cap.
  • Whether the same pattern holds when the consumer's effect is an external API call instead of a DynamoDB write that can share the inbox marker's transaction.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories