SNS fan-out
After this you can answer: Why publish one event to a topic instead of sending it to each worker?
Picture it
Section titled “Picture it”flowchart TB R["outbox relay"] -->|"Publish once"| T["SNS topic<br/>minitickets-dev-booking-events"] T -->|"copy"| J["SQS jobs queue<br/>(email)"] T -->|"copy"| K["SQS tickets queue"] J --> W["worker Lambda"] K --> L["tickets Lambda"]How it works
Section titled “How it works”- Publish once. The outbox relay calls
Publishon the topic with the message as JSON. - SNS copies. The topic has two subscriptions (a subscription = “deliver copies to this
endpoint”), both with protocol
sqs: the jobs queue and the tickets queue. - Raw delivery.
rawMessageDeliveryis on, so each queue receives exactly the JSON the api sent, not wrapped in an SNS envelope. The consumers parse one shape. - The queue must allow SNS in. Each queue has a resource policy (a policy attached to the
resource, saying who may use it):
sns.amazonaws.commaysqs:SendMessage, only whenaws:SourceArnis this topic andaws:SourceAccountis ours. - Each consumer runs alone. A broken email path does not stop tickets, and the other way round.
In MiniTickets
Section titled “In MiniTickets”Everything is in infra/src/jobs/booking-events.ts → createBookingEvents.
Each subscription is created only after its queue policy exists (dependsOn), because SNS would
otherwise fail to deliver. The topic ARN is handed to the api only when both subscriptions exist:
SNS happily accepts a message with zero subscribers, and the relay would then mark an undelivered
row as sent.
The topic is always on: it costs nothing while idle. The api task role may sns:Publish on this one
topic only.
| From → to | What happens | What allows it |
|---|---|---|
| Transactional outbox → Booking-events SNS topic | the relay publishes each pending row once to SNS |
|
| API ECS service → Booking-events SNS topiconly when computeEnabled = true | the outbox relay publishes each booking-confirmed event |
|
| Booking-events SNS topic → Jobs SQS queue | SNS delivers a copy to the email queue |
|
| Booking-events SNS topic → Tickets SQS queue | SNS delivers a copy to the tickets queue |
|
See it in AWS
Section titled “See it in AWS”SNS → Topics → minitickets-dev-booking-events → Subscriptions: two, protocol SQS, status
Confirmed.
aws sns list-subscriptions-by-topic --topic-arn <booking-events-topic-arn>
The queue policies and subscriptions: infra/src/jobs/booking-events.ts → createBookingEvents.
Break it
Section titled “Break it”| Symptom | Cause | Where to look |
|---|---|---|
| The relay logs “accepted by SNS”, but the queues stay empty | A queue policy does not allow sns.amazonaws.com from this topic | SQS → queue → Access policy |
| Tickets arrive but emails do not | One subscription is missing or points at another queue | SNS → topic → Subscriptions |
| The worker rejects every message as invalid | Raw delivery is off: the message arrives inside the SNS envelope | Subscription → Raw message delivery |
Say it in an interview
Section titled “Say it in an interview”Follow-ups
Section titled “Follow-ups”Why not let the Lambdas subscribe to SNS directly?
SNS would call them at once and retry on its own schedule. A queue in between holds messages while a consumer is down or slow, and gives each consumer batching, backoff and a DLQ.
How would you add a third consumer, for example analytics?
One more queue, its queue policy and one more subscription. The api and the other consumers do not change.
SNS or EventBridge?
EventBridge adds content-based routing rules, schemas and many targets. For one event type and two queues, SNS is simpler. It is a reasonable change if event types and routing rules grow.
Advanced: Details: ordering and duplicates
The topic and queues are standard (not FIFO). Delivery is at least once and in no fixed order. That is fine here: each message is about one booking, and both consumers are idempotent by booking id.
Checkpoint
Section titled “Checkpoint”Answer these without scrolling up. If one feels hard, reread that section.
- How many times does the api publish one booking event?
- What does
rawMessageDeliverychange? - Which two conditions does each queue policy check?
- Why does the api get the topic ARN only after both subscriptions exist?
- Why put a queue between SNS and each Lambda?
Redraw it from memory: outbox relay → SNS topic → (jobs queue → worker Lambda) and (tickets queue → tickets Lambda)
Try this: on the map, select the booking-events SNS topic, open Connections, and find the rule that lets SNS send to each queue
Read this book online: https://book.theversehub.in/