Build a Fault-Tolerant Event-Driven System
Use SNS, SQS, and Lambda to build a fan-out system with dead letter queues.
Introduction
30 Second Summary
When you order something online, a whole chain of events fires off behind the scenes. An email confirmation gets sent, inventory gets updated, and analytics get recorded. If one of those services crashes, you don't want the whole order to fail.
In this project, you will build a fault-tolerant event-driven system on AWS where a single "new order" event fans out to three independent consumer services. You'll then break one on purpose and prove that the others keep running.
What You'll Build
A notification fan-out system using Amazon SNS, Amazon SQS, and AWS Lambda that simulates a real e-commerce order pipeline with fault isolation between consumers.
By the end of this project, you'll have:
- A working fan-out system where a single published event triggers three independent consumer services through isolated message queues.
- A dead letter queue capturing failed messages while healthy consumers continue processing uninterrupted.
- A portfolio-ready architecture you can whiteboard in a system design interview to demonstrate decoupling, fault tolerance, and async messaging.
- Secret Mission: Add SNS message filtering so each queue only receives the event types it cares about.
Are there any prerequisites?
You'll need an AWS account with console access. Basic familiarity with navigating the AWS Console is helpful, but no prior experience with SNS, SQS, or Lambda is required.
Create Your Event Bus and Message Queues
When a customer places an order, multiple services need to know about it. Email needs to send a confirmation. Inventory needs to update stock. Analytics needs to record the event.
If these services talk directly to each other, one slow service can block everything else. In this step, you'll create the messaging infrastructure that decouples them. An SNS topic acts as the central event bus, and SQS queues give each consumer its own independent buffer.
In this step, get ready to:
- Create an SNS topic as the central event bus.
- Create three SQS queues for email, inventory, and analytics processing.
- Configure a dead letter queue to capture failed messages.
Create the SNS topic
Amazon SNS is a publish/subscribe messaging service. You publish a message once, and SNS delivers a copy to every subscriber. This is what makes the fan-out pattern possible.
- In the AWS Console search bar at the top, type SNS.
- Select Simple Notification Service from the results.
- In the left sidebar, click Topics.
- Click Create topic.
- Select Standard as the type.
- Enter OrderEvents in the Name field.
- Leave all other settings at their defaults and click Create topic.
You'll land on the topic details page. The ARN (Amazon Resource Name) is displayed near the top. Copy this value and save it somewhere handy, like a text file or sticky note. You'll need it in later steps.
What is an SNS topic?
Think of an SNS topic as a megaphone. When you publish a message to the topic, every subscriber hears it. Subscribers can be queues, email addresses, HTTP endpoints, or Lambda functions.
In this project, the OrderEvents topic is the single entry point for all order events. Three queues will subscribe to it, each getting their own copy of every message.
Create the processing queues
Each consumer service gets its own SQS queue. This means if one service falls behind or breaks, the others keep processing independently.
- In the AWS Console search bar, type SQS and select Simple Queue Service.
- Click Create queue.
- Select Standard as the type.
- Enter orders-email-queue in the Name field.
- Leave all other settings at their defaults (30-second visibility timeout, 4-day message retention).
- Click Create queue.
Now repeat the same process to create two more queues.
- Click Queues in the left sidebar to return to the queue list.
- Click Create queue again.
- Create a Standard queue named orders-inventory-queue with default settings.
- Click Create queue.
- Return to the queue list, click Create queue one more time.
- Create a Standard queue named orders-analytics-queue with default settings.
- Click Create queue.
What is Amazon SQS?
SQS is a message queue service. Messages sit in the queue until a consumer picks them up and processes them. If the consumer is slow or temporarily down, messages wait safely in the queue instead of being lost.
The 30-second visibility timeout means that after a consumer picks up a message, other consumers can't see it for 30 seconds. This prevents duplicate processing.
Why put SQS between SNS and Lambda?
SNS can trigger Lambda directly, but placing SQS in between gives you retry control, visibility timeout management, and dead letter queue support at the queue level. This is cleaner than relying on Lambda's built-in async invocation retries.
The SQS buffer also means messages persist if Lambda hits a concurrency limit. Nothing gets dropped.
Create the dead letter queue
A dead letter queue (DLQ) catches messages that fail processing after a set number of attempts. Instead of losing failed messages or retrying forever, they get moved to the DLQ where you can inspect and replay them.
- In the SQS console, click Create queue.
- Select Standard as the type.
- Enter orders-analytics-dlq in the Name field.
- Leave all settings at their defaults and click Create queue.
Now you'll configure the analytics processing queue to send failed messages to this DLQ.
- Click Queues in the left sidebar to return to the queue list.
- Click on orders-analytics-queue to open it.
- Click Edit.
- Scroll down to the Dead-letter queue section.
- Toggle the Dead-letter queue setting to Enabled.
- In the Choose queue dropdown, select orders-analytics-dlq.
- Set Maximum receives to 2.
- Click Save.
What does Maximum receives mean?
This is the number of times a message can be received (picked up by a consumer) before SQS moves it to the DLQ. Setting it to 2 means the message gets two chances to be processed successfully. If it fails both times, SQS moves it to orders-analytics-dlq so it isn't lost.
Don't poll messages from processing queues
Viewing messages in the SQS console counts toward the maximum receives. If you manually poll a processing queue to peek at messages, it uses up one of those attempts. Save your message inspections for the DLQ or for the final verification step.
Navigate back to Queues in the left sidebar. You should see all four queues listed: orders-email-queue, orders-inventory-queue, orders-analytics-queue, and orders-analytics-dlq.
Don't see all four queues?
Make sure you're in the correct AWS region. All queues must be created in the same region. Check the region selector in the top-right corner of the console.
If a queue is missing, click Create queue and create it again with the exact name shown above. Queue names are case-sensitive.
Still stuck? Help me troubleshoot my missing SQS queues.
Your messaging infrastructure is ready. You have an event bus (SNS topic) and four queues (three for processing, one for catching failures). Next up, you'll wire everything together by subscribing the queues to the SNS topic so they receive events automatically.
Wire the Fan-Out
Your event bus and message queues are ready. Now you need to connect them so that a single published event reaches all three consumer queues simultaneously.
This connection pattern is called fan-out. You publish one message to the SNS topic, and SNS delivers a copy to every subscribed SQS queue. Each queue processes independently, so one slow or broken consumer cannot block the others.
In this step, get ready to:
- Subscribe all three processing queues to the OrderEvents topic.
- Verify that AWS configured the correct permissions automatically.
- Test fan-out by publishing a single message and confirming it reaches all three queues.
Subscribe each queue to the SNS topic
A subscription tells SNS where to deliver messages. You will create one subscription per queue, pointing SNS at each queue's ARN.
- Return to the Amazon SNS console.
- In the left navigation pane, choose Topics.
- Click the OrderEvents topic to open it.
- In the Subscriptions tab, click Create subscription.
- For Protocol, choose Amazon SQS.
- For Endpoint, enter the ARN of orders-email-queue.
- Click Create subscription.
Finding a queue's ARN
To find the ARN of your SQS queue, open the Amazon SQS console, select the queue, and copy the ARN from the Details section at the top of the queue page.
- Return to the OrderEvents topic page.
- Click Create subscription again.
- For Protocol, choose Amazon SQS.
- For Endpoint, enter the ARN of orders-inventory-queue.
- Click Create subscription.
- Repeat once more for orders-analytics-queue: return to the topic, click Create subscription, choose Amazon SQS, enter the orders-analytics-queue ARN, and click Create subscription.
You should now see three subscriptions listed under the OrderEvents topic, all with status Confirmed.
Why are the subscriptions confirmed instantly?
Because you own both the SNS topic and the SQS queues in the same AWS account, subscriptions are confirmed automatically. Cross-account subscriptions require a separate confirmation step.
Verify the access policy
For SNS to deliver messages into your queues, each queue needs a permission policy allowing the topic to send. AWS adds this policy automatically when you subscribe from the same account.
- Open the Amazon SQS console.
- Click on orders-email-queue to open it.
- Select the Access policy tab.
- Confirm that the JSON policy includes an Allow statement with action sqs:SendMessage and a condition matching the OrderEvents topic ARN.
- Repeat for orders-inventory-queue and orders-analytics-queue.
What does this policy do?
The sqs:SendMessage permission lets the SNS service principal push messages into the queue. The aws:SourceArn condition restricts this permission so that only the OrderEvents topic can deliver messages. No other topic or service can send to your queue.
Don't see the policy statement?
If the Access policy tab shows no SNS-related statement, the subscription may not have completed successfully. Return to the SNS console, delete the subscription, and re-create it making sure you paste the correct queue ARN.
Help me verify my SQS access policy for SNS subscriptions
Test the fan-out with a manual publish
Time to see the fan-out pattern in action. You will publish a single message to the topic and verify that all three queues receive their own copy.
- Return to the Amazon SNS console and open the OrderEvents topic.
- Click Publish message.
- In the Subject field, enter New Order.
- In the Message body field, enter the following JSON:
{"orderId": "ORD-001", "item": "Laptop", "quantity": 1}
- Click Publish message.
SNS has now delivered a copy of this message to each of the three subscribed queues.
- Open the Amazon SQS console.
- In the Queues list, check the Messages available column for each of your three processing queues.
- All three queues should show 1 message available.
What is the Messages available column?
This column shows the approximate number of messages sitting in the queue waiting to be processed. Checking this number is safe. It does not count as receiving or consuming a message.
Do not poll for messages on the analytics queue
Clicking Send and receive messages and then Poll for messages inside a queue does count as receiving. Each receive increments the message's receive count.
The orders-analytics-queue has a maximum receive count of 2 before messages move to the dead letter queue. Save your receives for when Lambda processes the messages in the next step.
Queues showing 0 messages?
If you do not see 1 message in each queue, wait 10 seconds and refresh the page. SNS delivery is near-instant but the console counter can take a moment to update.
If still 0, return to the SNS console and check that all three subscriptions show Confirmed status. A subscription stuck in Pending confirmation means the queue ARN was entered incorrectly.
Help me figure out why my SQS queues aren't receiving messages from SNS
One publish, three deliveries. Your fan-out is wired and working. Next up, you will create Lambda functions that consume messages from each queue and see what happens when one of them breaks.
Create Lambda Consumer Functions
Your fan-out wiring is complete. A single message published to OrderEvents reaches all three queues.
Now you need something to actually process those messages. In this step, you will create three Lambda functions that simulate independent microservices: email, inventory, and analytics. One of them will be deliberately broken so you can observe fault tolerance in action later.
In this step, get ready to:
- Create three Lambda functions that act as independent consumer services.
- Connect each SQS queue to its corresponding Lambda function with an event source mapping.
- Verify that triggers are active and Lambda is polling your queues.
Create the email handler Lambda
Each consumer service lives in its own Lambda function. You will start with the email handler, which simulates sending an order confirmation email.
- Navigate to the Lambda service in the AWS Console by searching for Lambda in the top search bar.
- Click Create function.
- Select Author from scratch.
- Enter order-email-handler as the Function name.
- For Runtime, choose Python 3.13.
- Leave Architecture set to x86_64.
- Click Create function.
After creation, the console opens the function's code editor with a default lambda_function.py file.
- Select the Code tab if it is not already visible.
- Delete all the default code in lambda_function.py and replace it with the following:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
print(f"[EMAIL SERVICE] Sending order confirmation email for: {json.dumps(message)}")
return {'statusCode': 200}
What does this code do?
When SQS triggers a Lambda function, it passes an event containing a Records array. Each record's body is the raw SQS message.
Because the SQS message originally came from SNS, the body is actually an SNS envelope. The real order data lives inside the envelope's Message field. That is why you parse twice: once for the envelope, once for the payload.
- In the DEPLOY section of the code editor, click Deploy.
You should see a confirmation banner indicating your changes have been deployed.
Don't see the Deploy button?
Make sure you are on the Code tab. The DEPLOY section sits above the code editor. If you accidentally navigated away, click the function name in the breadcrumb at the top to return.
Help me find the Deploy button in the Lambda console
Create the inventory and analytics handler Lambdas
You will now create two more Lambda functions. The inventory handler processes stock updates. The analytics handler will deliberately crash to simulate a broken service.
- Return to the Lambda Functions page by clicking Functions in the left navigation panel.
- Click Create function.
- Select Author from scratch.
- Enter order-inventory-handler as the Function name.
- For Runtime, choose Python 3.13.
- Leave Architecture set to x86_64.
- Click Create function.
- Delete all the default code in lambda_function.py and replace it with the following:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
print(f"[INVENTORY SERVICE] Updating stock for: {json.dumps(message)}")
return {'statusCode': 200}
- Click Deploy to save your changes.
Now create the deliberately broken analytics handler.
- Return to the Functions page again.
- Click Create function.
- Select Author from scratch.
- Enter order-analytics-handler as the Function name.
- For Runtime, choose Python 3.13.
- Leave Architecture set to x86_64.
- Click Create function.
- Delete all the default code in lambda_function.py and replace it with the following:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
raise Exception(f"Simulated failure: Analytics service is down! Message: {json.dumps(message)}")
return {'statusCode': 200}
Why is this function broken on purpose?
The analytics handler raises an exception every time it runs. This simulates a real-world scenario where one microservice goes down while others remain healthy.
In the next step, you will observe how the email and inventory services continue processing orders normally while the analytics failures are safely captured in the dead letter queue.
- Click Deploy to save your changes.
You should now have three deployed Lambda functions. Verify by returning to the Functions page and confirming all three appear in the list.
✔️ Awesome, I've got everything!
Great. Confirm all three functions show Python 3.13 under the Runtime column on the Functions page.
ⓧ I'd like to double check the full code
Here is the code for each Lambda function:
order-email-handler:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
print(f"[EMAIL SERVICE] Sending order confirmation email for: {json.dumps(message)}")
return {'statusCode': 200}
order-inventory-handler:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
print(f"[INVENTORY SERVICE] Updating stock for: {json.dumps(message)}")
return {'statusCode': 200}
order-analytics-handler (deliberately broken):
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
raise Exception(f"Simulated failure: Analytics service is down! Message: {json.dumps(message)}")
return {'statusCode': 200}
Connect SQS queues to Lambda functions
Each Lambda function needs an event source mapping that tells it to poll a specific SQS queue. When you add an SQS trigger via the Lambda console, Lambda automatically creates the necessary execution role permissions.
- On the Functions page, click order-email-handler to open it.
- In the Function overview pane, click Add trigger.
- In the trigger type dropdown, select SQS.
- In the SQS queue field, search for and select orders-email-queue.
- Leave all other settings at their defaults.
- Click Add.
Repeat this process for the other two functions.
- Open order-inventory-handler and add an SQS trigger pointing to orders-inventory-queue.
- Open order-analytics-handler and add an SQS trigger pointing to orders-analytics-queue.
What is an event source mapping?
An event source mapping is the connection between an SQS queue and a Lambda function. Lambda continuously polls the queue for new messages and invokes your function with a batch of messages when they arrive.
When you add the trigger via the console, Lambda also grants itself the permissions it needs to read from SQS (sqs:ReceiveMessage, sqs:DeleteMessage, sqs:GetQueueAttributes) by updating the function's execution role automatically.
- Verify each trigger by opening each function, clicking the Configuration tab, then Triggers.
- Confirm each function shows an SQS trigger with a status of Enabled.
What about the test message already in my queues?
The ORD-001 message you published earlier is still sitting in each queue. As soon as you enable the trigger, Lambda begins polling and will process that message automatically.
The email and inventory handlers will log the message successfully. The analytics handler will fail on it, triggering the retry and DLQ behavior you will observe in the next step.
Trigger showing as Disabled or Creating?
It can take up to one minute for the trigger status to change from Creating to Enabled. Refresh the page after a moment.
If the trigger remains disabled, check that the queue and Lambda function are in the same AWS Region.
Help me troubleshoot my SQS trigger status
Your three consumer services are deployed and connected to their queues. Next up, you will publish a fresh order event and watch how the healthy consumers process it while the broken analytics handler triggers the dead letter queue.
Test the System and Observe Fault Tolerance
Your three Lambda consumers are wired up and ready to process messages from their queues. Now it's time to put the entire system to the test.
You'll publish a new order event and watch how the system handles a broken consumer. This is where fault isolation proves its value.
In this step, get ready to:
- Publish a test order event through SNS.
- Verify healthy consumers processed the message successfully.
- Observe the analytics failure and dead letter queue in action.
Publish a new order event
With all three consumers connected, a single message published to your SNS topic will fan out to all three queues simultaneously. Let's trigger the pipeline and see what happens.
- Return to the Amazon SNS console.
- In the left navigation pane, select Topics.
- Click on OrderEvents to open your topic.
- Click Publish message.
- In the Subject field, enter New Order.
- In the Message body field, enter the following JSON:
{"orderId": "ORD-002", "item": "Headphones", "quantity": 2}
- Click Publish message.
You should see a green success banner confirming the message was published.
What just happened?
SNS received your message and delivered a copy to each of the three subscribed SQS queues. Each queue now has its own independent copy of the order event, ready for its Lambda consumer to process.
Wait about 30 seconds for the Lambda functions to pick up and process the messages from their queues.
Verify healthy consumers processed successfully
Each Lambda function writes logs to its own CloudWatch log group. The log group name follows the pattern /aws/lambda/<function-name>.
- Open the CloudWatch console by searching for CloudWatch in the AWS search bar.
- In the left navigation pane, expand Logs and select Log groups.
- Click on the log group named /aws/lambda/order-email-handler.
- Click the most recent log stream (the one at the top of the list).
- Look for a log entry containing [EMAIL SERVICE] Sending order confirmation email for: followed by your order JSON.
This confirms the email handler processed the order successfully.
- Navigate back to Log groups.
- Click on /aws/lambda/order-inventory-handler.
- Click the most recent log stream.
- Look for a log entry containing [INVENTORY SERVICE] Updating stock for: followed by your order JSON.
Both healthy consumers processed the same order event independently and successfully.
Why do both handlers show success?
Each queue holds its own copy of the message. Each Lambda function processes only from its own queue. A failure in one consumer has zero impact on the others.
Don't see any log streams?
Lambda logs can take up to 5-10 minutes to appear in CloudWatch after invocation. Wait a few minutes and refresh the page.
If no log group exists at all, check that your Lambda function's event source mapping is enabled in the Lambda console under the Configuration > Triggers tab.
help me troubleshoot why my Lambda function logs aren't appearing in CloudWatch
Observe the failure and dead letter queue
The analytics handler was deliberately written to raise an exception. Let's see how SQS handles a consumer that keeps failing.
- Navigate back to Log groups in the CloudWatch console.
- Click on /aws/lambda/order-analytics-handler.
- Click the most recent log stream.
- Look for error entries containing Simulated failure: Analytics service is down!.
You should see the exception logged multiple times. SQS delivered the message to the Lambda function, the function failed, and SQS retried delivery after the visibility timeout.
How does the dead letter queue work?
You configured orders-analytics-queue with a maximum receives value of 2. This means SQS attempts to deliver the message to the Lambda consumer twice.
After both attempts fail, SQS automatically moves the message to orders-analytics-dlq instead of discarding it. The message is preserved for inspection and recovery.
Now let's confirm the failed message landed in the dead letter queue.
- Open the Amazon SQS console.
- Select orders-analytics-dlq from the queue list.
- Check the Messages available column. You should see at least 1 message.
- Click Send and receive messages.
- Click Poll for messages.
- Click on the message ID that appears to inspect the message body.
You should see the original SNS notification envelope containing your order JSON with ORD-002 inside the Message field.
Polling the DLQ is safe
Polling messages from the DLQ is safe because there is no Lambda consumer attached to it. However, do not poll from the processing queues manually. Viewing a message in the SQS console counts toward the queue's maximum receives count, which can accidentally push messages to the DLQ early.
What does this prove?
Your email and inventory handlers processed the order event successfully. The analytics handler failed repeatedly. Yet the healthy consumers were completely unaffected by the analytics failure.
This is fault isolation in action. Because each consumer has its own queue, a crash in one service cannot block or disrupt the others. The failed messages are safely captured in the DLQ for later recovery.
DLQ still shows 0 messages?
The message needs to be received twice before SQS moves it to the DLQ. With a 30-second visibility timeout, this takes about 1-2 minutes after the initial publish. Wait and refresh the queue list.
Also confirm your orders-analytics-queue has the dead-letter queue configured by checking the queue's Dead-letter queue section under Edit.
help me figure out why messages aren't appearing in my SQS dead letter queue
You've proven that your system handles failure gracefully. Next up, you'll fix the broken analytics service and redrive the failed messages from the DLQ back for successful processing.
Fix the Failure and Redrive Messages
In the previous step, you proved that fault isolation works. The email and inventory services kept humming along while the analytics handler crashed repeatedly.
Now it's time to complete the recovery cycle. You'll fix the broken analytics Lambda, then use the DLQ redrive feature to replay the failed messages through the repaired consumer.
In this step, get ready to:
- Fix the broken analytics Lambda function.
- Redrive failed messages from the dead letter queue back to the source queue.
- Verify all three consumers are processing successfully.
Fix the broken analytics Lambda
The analytics handler is still raising an exception on every message. You need to swap that error line for a working log statement.
- Return to the Lambda console.
- Click on order-analytics-handler to open the function.
- In the Code source editor, find this line:
raise Exception(f"Simulated failure: Analytics service is down! Message: {json.dumps(message)}")
- Replace that entire line with the following:
print(f"[ANALYTICS SERVICE] Recording event data: {json.dumps(message)}")
- Click the Deploy button in the Code source pane.
- Wait for the green confirmation banner that says the function has been updated.
What did this fix do?
The broken version threw an exception on every message, which caused SQS to retry the message and eventually move it to the dead letter queue.
The fixed version simply logs the event data. Now when messages arrive, the function processes them and returns a success response instead of crashing.
✔️ Awesome, I've got everything!
Great. Double check you've deployed the function and seen the green confirmation banner.
ⓧ I'd like to double check the full code
Here's the complete fixed code for order-analytics-handler:
import json
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message = json.loads(body['Message'])
print(f"[ANALYTICS SERVICE] Recording event data: {json.dumps(message)}")
return {'statusCode': 200}
Redrive messages from the DLQ
The failed messages are sitting in orders-analytics-dlq waiting to be reprocessed. SQS has a built-in redrive feature that sends those messages back to the original source queue for another attempt.
- Switch to the SQS console.
- Click on orders-analytics-dlq to open it.
- Click Start DLQ redrive.
- Select Redrive to source queue(s).
- For velocity control, choose System optimized.
- Click Redrive messages.
What does redrive do?
Redrive takes the messages stuck in the dead letter queue and pushes them back to the original source queue (orders-analytics-queue). Since the Lambda trigger is still active on that queue, the fixed function picks up and processes those messages automatically.
System optimized velocity lets AWS manage the throughput rate. For this project's small message volume, the redrive completes in seconds.
Don't see the Start DLQ redrive button?
Make sure you selected orders-analytics-dlq (the dead letter queue), not orders-analytics-queue (the processing queue). The redrive button only appears on a queue that is configured as a DLQ.
help me find the DLQ redrive button in the SQS console
Verify recovery
The redrive sent those failed messages back through the system. Now you need to confirm three things: the analytics handler processed them, the DLQ is empty, and a brand new message flows through all three consumers.
- Open CloudWatch and navigate to Log groups.
- Click on the log group for order-analytics-handler.
- Open the most recent log stream.
- Confirm you see a log entry containing [ANALYTICS SERVICE] Recording event data with the ORD-002 order details.
What should I see?
The log entry proves that the previously failed ORD-002 message was successfully reprocessed by the fixed analytics handler. This is the message that originally crashed the function and ended up in the DLQ.
- Return to the SQS console and click on orders-analytics-dlq.
- Check the Messages available column. It should show 0.
The DLQ is empty because the redrive sent all messages back to the source queue, and the fixed Lambda consumed them successfully.
- Now publish one final test message to confirm the full system is healthy.
- Switch to the SNS console and open the OrderEvents topic.
- Click Publish message.
- Enter New Order in the Subject field.
- Enter the following in the Message body:
{"orderId": "ORD-003", "item": "Keyboard", "quantity": 1}
- Click Publish message.
- Wait about 30 seconds, then check CloudWatch Logs for all three handlers.
- Confirm order-email-handler, order-inventory-handler, and order-analytics-handler all show successful log entries for ORD-003.
Why does this final test matter?
The redrive only proved that old failed messages could be recovered. This fresh publish proves the system is fully healed. All three consumers are back online and processing new events without any intervention.
This is the complete recovery workflow in production: detect failure via the DLQ, fix the broken service, redrive the stuck messages, and confirm the system is healthy again.
Analytics handler still showing errors?
Make sure you clicked Deploy after editing the code. Saving the file without deploying does not update the live function.
Also check that you replaced the entire raise Exception(...) line and not just part of it. The function should have a print(...) statement in its place.
help me debug why my analytics handler is still failing after I edited the code
You just completed the full recovery cycle. You detected a failure through the DLQ, fixed the broken consumer, replayed the lost messages, and confirmed the system is healthy. Next up, the Secret Mission challenges you to add message filtering so each queue only receives the events it actually cares about.
Secret mission
Add SNS Message Filtering
Right now, every queue receives every message. In a real system, the email service doesn't need shipping updates and the inventory service doesn't care about cancellations. Can you configure SNS filter policies so each queue only receives the event types it actually needs?
Clean Up Your Resources
Clean Up Your Resources
All resources in this project fall within the AWS Always Free tier. There are no ongoing costs as long as you stay within the free tier limits, which this project's usage will not exceed.
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- 1 SNS Standard topic (OrderEvents).
- 4 SQS Standard queues (orders-email-queue, orders-inventory-queue, orders-analytics-queue, orders-analytics-dlq).
- 3 Lambda functions (order-email-handler, order-inventory-handler, order-analytics-handler).
- 3 IAM execution roles (auto-created by Lambda).
- 3 SQS event source mappings (deleted when Lambda functions are deleted).
Keep everything running
No action needed. Choose this if you plan to experiment further with your fan-out system.
- All resources stay within the Always Free tier (1,000,000 requests per month each for SNS, SQS, and Lambda).
- The event source mappings will poll SQS periodically, but this polling counts toward your free tier Lambda invocations and will not generate charges at this project's usage level.
- You can continue publishing test messages to OrderEvents at any time.
Pause - I'll come back to this later
Shut down background polling to reduce unnecessary activity while keeping your resources intact.
- Open the Lambda console and select each of your three functions.
- Go to the Configuration tab, then Triggers.
- Select the SQS trigger and choose Disable. This stops Lambda from polling the queue.
- When you return, re-enable the triggers to resume processing.
Why disable triggers instead of deleting?
Disabling the event source mapping keeps your entire architecture wired up. When you come back, just flip the trigger back on and publish a new message to pick up where you left off.
Delete - I don't want to use this again
Remove all project resources completely. Follow this order to avoid errors from dangling references.
Delete the three Lambda functions:
- Open the Functions page of the Lambda console.
- Select order-email-handler.
- Choose Actions, then Delete.
- Type confirm in the text input field and choose Delete.
- Repeat for order-inventory-handler and order-analytics-handler.
Delete the four SQS queues:
- Open the Amazon SQS console and choose Queues in the navigation pane.
- Select orders-email-queue.
- Choose Delete.
- In the Delete queue dialog box, enter delete to confirm, then choose Delete.
- Repeat for orders-inventory-queue, orders-analytics-queue, and orders-analytics-dlq.
Delete the SNS topic:
- Open the Amazon SNS console and choose Topics in the navigation pane.
- Select the OrderEvents topic.
- Choose Delete.
- In the Delete topic dialog box, enter delete me and choose Delete.
Delete the IAM execution roles (optional):
- Open the Roles page of the IAM console.
- Select the execution role for each function (e.g. order-email-handler-role).
- Choose Delete.
- Enter the name of the role in the text input field and choose Delete.
- Repeat for order-inventory-handler-role and order-analytics-handler-role.
Nice Work!
Nice Work!
You just built a production-pattern event-driven architecture on AWS. Your system fans out a single order event to three independent consumers, isolates failures with dead letter queues, and recovers gracefully through message redrives.
You've learned how to:
- Build an event-driven fan-out system using SNS as a pub/sub event bus that delivers messages to multiple SQS queues simultaneously.
- Deploy serverless Lambda functions that process messages asynchronously from decoupled queues, keeping each consumer independent.
- Prove fault isolation with a dead letter queue that captures failed messages, then perform a DLQ redrive to recover them after fixing the broken consumer.
- Secret Mission: Implemented SNS message filtering to route events to only the services that need them.
Ready to quiz yourself?