DynamoDB
After this you can answer: Why does the worker remember which jobs it already did, and why in DynamoDB?
Picture it
Section titled “Picture it”flowchart TB M["message:<br/>booking confirmed"] --> G{"GetItem<br/>jobKey done?"} G -->|"yes"| S["skip, report success"] G -->|"no"| E["send the email (SES)"] E --> P["PutItem only if<br/>attribute_not_exists(jobKey)"]How it works
Section titled “How it works”- Pick a business key. The job key is
booking.confirmed:<bookingId>, not the message id. The outbox may publish the same booking twice under two message ids; both map to one key. - Check first. A
GetItemwithConsistentRead, so a write a moment ago is seen. - Send, then record. After SES accepts the email, a
PutItemwith the conditionattribute_not_exists(jobKey): of two workers recording at once, exactly one wins. - Forget later. Each item has
expiresAt, 30 days ahead. DynamoDB’s TTL (time to live) deletes old items for free, some time after that.
Sending before recording is a choice: a crash in between can send a rare duplicate, but never loses an email. Recording first would lose it. So this is “effectively once”, not exactly once.
In MiniTickets
Section titled “In MiniTickets”| Table | Keys | Written by | Read by |
|---|---|---|---|
minitickets-dev-processed-jobs | jobKey | worker (PutItem) | worker (GetItem) |
minitickets-dev-tickets | userId + ticketId; index by-event-date | tickets Lambda | api, Query only |
Both use on-demand billing (PAY_PER_REQUEST): no capacity to plan, nothing to pay while idle. The
api’s “My tickets” is one Query on the signed-in user’s partition, never a scan.
| From → to | What happens | What allows it |
|---|---|---|
| Worker Lambda → Processed-jobs tableonly when computeEnabled = true | GetItem before sending; a conditional PutItem after sending |
|
See it in AWS
Section titled “See it in AWS”DynamoDB → Tables → minitickets-dev-processed-jobs → Explore items; Additional settings → Time to
Live.
aws dynamodb describe-table --table-name minitickets-dev-processed-jobs
Break it
Section titled “Break it”| Symptom | Cause | Where to look |
|---|---|---|
Every email job fails; AccessDenied on dynamodb:GetItem | The worker role names another table ARN | IAM → worker role → policy |
| A user gets the same email twice | Sent, but the PutItem failed or timed out, so the retry sent again | Worker log for that correlation id |
| My tickets: AccessDenied in the api log | The code tried something other than Query | api task role |
Say it in an interview
Section titled “Say it in an interview”Follow-ups
Section titled “Follow-ups”Why DynamoDB and not a table in Postgres?
The Lambda runs outside the VPC and cannot reach Postgres. DynamoDB needs no connection pool for a bursty Lambda, and offers the conditional write and TTL we need.
How would you close the two-workers window?
A lease: write “in progress until now + 60 s” with a conditional write before sending. A second worker backs off. It still cannot close the crash window, and costs one more write per job.
What if the processed-jobs table were lost?
Only possible duplicate emails. That is why point-in-time recovery is off in dev; production would weigh it differently.
Advanced: Details: the tickets table
Partition key userId, sort key ticketId, global secondary index by-event-date (eventDate +
eventDateKey). Server-side encryption is on. It stays on when the compute and data switches are
off, so issued tickets survive
(infra/src/tickets/storage.ts → createTicketStorage).
Checkpoint
Section titled “Checkpoint”Answer these without scrolling up. If one feels hard, reread that section.
- Why is the job key the booking id and not the message id?
- What does
attribute_not_exists(jobKey)guarantee? - Why send first and record second?
- What does TTL do, and what does it cost?
- Which single operation may the api run on the tickets table?
Redraw it from memory: message → GetItem (done?) → yes: skip / no: send with SES → conditional PutItem → TTL deletes after 30 days
Try this: on the map, select the processed-jobs table and open 'Who can touch me' in the Permissions tab
Read this book online: https://book.theversehub.in/