Custom Events and Dead Lettering
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Lesson: Mastering Custom Events and Dead Lettering in Azure
Introduction: The Architecture of Reactive Systems
In modern cloud computing, building applications that react to changes in real-time is no longer a luxury; it is a fundamental requirement. Whether you are synchronizing databases, triggering workflows upon file uploads, or updating user interfaces when backend states change, event-driven architecture provides the foundation for decoupled, scalable systems. However, the true challenge in these systems isn't just sending events—it is ensuring that those events are delivered reliably and handling the inevitable failures that occur when things go wrong.
This lesson explores two critical components of Azure’s event-driven ecosystem: Custom Events and Dead Lettering. Custom Events allow you to define your own domain-specific signals within Azure Event Grid, moving beyond simple system-generated notifications. Dead Lettering acts as your safety net, capturing events that could not be processed for various reasons, ensuring that no data is lost in transit. By mastering these two concepts, you move from building "happy path" applications to creating resilient, enterprise-grade systems capable of handling the complexities of distributed computing.
Part 1: Understanding Custom Events in Azure Event Grid
Azure Event Grid is a fully managed event routing service that enables uniform event consumption using a publish-subscribe model. While many developers start by using system events (like those generated by Azure Storage or Resource Groups), Custom Events are where you integrate your own business logic into the fabric of the cloud.
What Constitutes a Custom Event?
A custom event is a JSON object that follows the CloudEvents 1.0 schema or the native Event Grid schema. When you publish a custom event, you are essentially telling the cloud, "Something happened in my application, and here is the context."
To define a custom event, you must include several mandatory fields:
- ID: A unique identifier for the event.
- Subject: A path or identifier for the subject of the event (e.g., "orders/12345").
- Data: The actual payload containing the information you want to pass.
- EventType: A string that categorizes the event (e.g., "OrderPlaced" or "InventoryUpdated").
- EventTime: The timestamp of when the event occurred.
Why Use Custom Events?
Custom events allow you to decouple your services. Instead of Service A calling Service B directly via an HTTP request, Service A emits an event. Service B, C, and D can then subscribe to that event independently. This reduces tight coupling, makes your system easier to test, and allows you to add new functionality without modifying the original service that triggered the event.
Callout: Custom Events vs. System Events System events are triggered by Azure resources (e.g., a blob being created in a container). They are predefined and managed by the platform. Custom events are generated by your own applications or third-party tools. You control the schema, the timing, and the meaning of these events, giving you complete flexibility over your domain logic.
Part 2: Publishing Custom Events
To publish custom events, you need an Event Grid Topic. This topic acts as the endpoint where your applications send their events.
Step-by-Step: Setting Up a Custom Topic
- Create the Topic: Navigate to the Azure Portal, search for "Event Grid Topics," and create a new one. Select your resource group and region.
- Retrieve Credentials: Once the topic is created, locate the "Access Keys" and the "Topic Endpoint" in the overview blade.
- Draft the Payload: Prepare your JSON data according to the schema.
- Send the Event: Use an HTTP POST request to the topic endpoint, including your access key in the
aeg-sas-keyheader.
Code Example: Publishing with C#
Using the Azure.Messaging.EventGrid library, publishing events becomes straightforward. Here is how you might trigger a custom event from a backend service:
using Azure;
using Azure.Messaging.EventGrid;
// Define the endpoint and key
string topicEndpoint = "https://your-topic.region.eventgrid.azure.net/api/events";
string topicKey = "your-access-key-here";
// Create the publisher client
EventGridPublisherClient client = new EventGridPublisherClient(
new Uri(topicEndpoint),
new AzureKeyCredential(topicKey));
// Create a custom event
EventGridEvent customEvent = new EventGridEvent(
subject: "orders/12345",
eventType: "OrderPlaced",
dataVersion: "1.0",
data: new { OrderId = 12345, Customer = "John Doe", Total = 99.99 });
// Publish the event
await client.SendEventAsync(customEvent);
In this example, the EventGridPublisherClient handles the heavy lifting of authentication and serialization. Notice how we pass an anonymous object as the data parameter; the library automatically serializes this into the JSON format required by Event Grid.
Part 3: The Role of Dead Lettering
Even in the most well-designed system, events will occasionally fail to reach their destination. Perhaps the destination endpoint is down, the authentication token has expired, or the event payload is malformed. Without a strategy for handling these failures, these events simply disappear, leading to data loss and "silent" failures that are notoriously difficult to debug.
What is Dead Lettering?
Dead Lettering is a feature in Event Grid that allows you to store undelivered events in an Azure Storage account. When an event fails to be delivered after the maximum number of retry attempts, Event Grid sends that event to the designated storage container. This creates a "waiting room" for failed events where you can inspect, fix, and potentially reprocess them.
Why You Need Dead Lettering
- Data Integrity: You ensure that no business-critical event is lost.
- Debugging: By inspecting dead-lettered events, you can identify why they failed—perhaps there was a schema mismatch or a network configuration error.
- Auditability: You maintain a record of failed processing attempts, which is often a requirement for compliance and system reliability.
Callout: Retry Policy vs. Dead Lettering It is important to distinguish between the two. Event Grid has a built-in retry policy (exponential backoff). It will attempt to deliver the event for up to 24 hours. Dead Lettering only kicks in after the retry policy has exhausted its attempts. It is the final destination for events that simply cannot be delivered.
Part 4: Implementing Dead Lettering
To implement dead lettering, you need to configure an Azure Storage account to act as the destination for failed events.
Step-by-Step: Enabling Dead Lettering
- Create a Storage Account: Ensure you have a standard storage account created in the same region as your event subscription.
- Create a Container: Inside the storage account, create a blob container to hold the dead-lettered events.
- Configure the Subscription: Go to your Event Grid subscription settings.
- Enable Dead Lettering: Under the "Dead Letter" tab, check the "Use dead lettering" box.
- Select Destination: Choose your storage account and the specific container you created.
Handling the Dead Lettered Events
Once an event lands in your storage account, it is saved as a JSON file. You can then write a separate monitoring service (like an Azure Function) that triggers when a new blob is added to this container. This function could then:
- Log the error to a monitoring tool like Application Insights.
- Notify an administrator via email or Slack.
- Attempt to fix the data and republish the event to the topic.
Part 5: Best Practices and Industry Standards
Managing custom events and dead lettering requires a disciplined approach to ensure reliability and maintainability. Follow these industry-standard practices to keep your event-driven systems running smoothly.
1. Schema Validation
Always validate your event schemas before publishing. If you publish an event that doesn't match the expected structure of your subscribers, the subscriber will fail, and the event will eventually be dead-lettered. Use schema registries or shared libraries to ensure producers and consumers agree on the data structure.
2. Idempotent Consumers
Design your event consumers to be idempotent. This means that if an event is processed twice, the end result is the same as if it were processed once. In distributed systems, retries are common, and you must ensure your system can handle duplicate events without duplicating side effects (like charging a customer's credit card twice).
3. Monitoring and Alerting
Do not rely on manual checks to see if events are failing. Set up alerts on the "Dead Letter Events" metric for your Event Grid subscription. If the count of dead-lettered events increases, your team should receive an immediate notification.
4. Meaningful Subjects
Use a consistent naming convention for your event subjects. For example, use resource-type/resource-id. This makes it much easier to filter events or search through dead-lettered logs when trying to troubleshoot a specific business process.
5. Keep Payloads Lean
While you want to provide enough context in your event, avoid sending massive objects. If you need to send a full database record, consider sending a reference (a URL or ID) in the event, and let the consumer fetch the details from the source of truth if needed.
Part 6: Comparison of Event Handling Strategies
When building systems that need to handle failures, it is helpful to understand how different components compare.
| Feature | Event Grid Dead Lettering | Service Bus Dead Letter Queue |
|---|---|---|
| Primary Use | Event routing/notifications | Durable messaging/queues |
| Failure Handling | Storage account (blob) | Internal queue storage |
| Retry Logic | Built-in (24 hours) | Configurable (delivery count) |
| Data Format | CloudEvents / Custom JSON | Arbitrary binary/string/object |
| Best For | Decoupled, reactive workflows | Transactional, ordered processing |
Warning: The 24-Hour Rule Remember that Event Grid only attempts delivery for 24 hours. If your destination is down for a scheduled maintenance window longer than this, your events will be dead-lettered. Plan your system's availability and recovery time objectives accordingly.
Part 7: Common Pitfalls and How to Avoid Them
Even experienced developers fall into common traps when implementing event-driven systems. Here is how to avoid them.
Pitfall 1: Ignoring the Dead Letter Container
The most common mistake is enabling dead lettering but never checking the container. You end up with a storage account filling up with failed events that no one is looking at. Treat your dead-letter container as a production log—monitor it, clear it, and act on the data within it.
Pitfall 2: Over-reliance on Retries
While retries are helpful for transient network issues, they can be harmful if the failure is caused by a logic error (e.g., a division by zero error in your code). If an event is fundamentally broken, retrying it 30 times will not fix it. Implement "poison message" handling where you catch specific exceptions and immediately move the event to a failure log instead of letting it exhaust all retry attempts.
Pitfall 3: Tight Coupling via Events
Sometimes developers create a chain of events that is so complex it becomes a "distributed monolith." If Event A triggers Event B, which triggers Event C, and a failure in C causes a chain reaction, you have effectively created a synchronous system disguised as an asynchronous one. Keep your event chains short and your services as autonomous as possible.
Pitfall 4: Security Oversights
Ensure your Event Grid topic is secured using Azure Active Directory (RBAC) or shared access keys. Do not expose your topic endpoints to the public internet without proper authentication. If you are publishing from an internal network, consider using Azure Private Link to keep your event traffic off the public network entirely.
Part 8: Advanced Scenarios and Patterns
The "Compensating Transaction" Pattern
When an event triggers a multi-step process, and one step fails, you may need a compensating transaction. For instance, if an event triggers a "Reserve Inventory" step and a "Process Payment" step, and the payment fails, you must emit a new event to "Release Inventory." This pattern is essential for maintaining consistency across independent services.
Filtering Events
You don't always want every subscriber to receive every event. Event Grid supports advanced filtering, allowing you to subscribe to events based on the EventType, Subject, or even properties within the Data payload. This reduces the load on your consumers and prevents unnecessary processing.
Example: Filtering by Data Property
If your OrderPlaced event contains a Priority field, you can create a subscription that only triggers for "High" priority orders. This is done in the subscription filter settings:
{
"advancedFilters": [
{
"operatorType": "StringContains",
"key": "Data.Priority",
"values": ["High"]
}
]
}
This configuration ensures that only relevant events reach your high-priority processing service, saving compute resources and reducing noise.
Part 9: Practical Exercise Walkthrough
To solidify your understanding, let's walk through a scenario: A retail application.
- Producer: The Checkout Service publishes an
OrderPlacedevent. - Consumer 1: The Email Service sends a confirmation to the customer.
- Consumer 2: The Inventory Service reserves the items.
Scenario: The Email Service is down.
If the Email Service is offline, Event Grid will retry the delivery. After 24 hours of failed retries, the event is moved to the dead-letter storage.
Your Action Plan:
- Monitor: An alert triggers because the dead-letter count in your storage account is greater than zero.
- Inspect: You download the JSON file from the blob storage. You see the
OrderPlacedevent forOrder 12345. - Resolve: You verify the Email Service is back online.
- Republish: You use a simple script to read the dead-lettered JSON and push it back to the Event Grid topic.
- Verify: The Email Service picks up the event, and the customer receives their confirmation (albeit late).
This workflow demonstrates why dead lettering is not just a safety feature, but a core component of your operational support model.
Part 10: Key Takeaways
As we conclude this lesson, keep these fundamental principles in mind for your future projects:
- Custom Events provide flexibility: They allow you to define the language of your domain, decoupling services and enabling a truly reactive architecture.
- Reliability is non-negotiable: In a distributed system, failures are guaranteed. Dead lettering is your primary tool for managing these failures and ensuring data durability.
- Design for Failure: Always build your consumers to be idempotent. Expect that an event might be delivered more than once or arrive in an unexpected order.
- Monitor everything: Dead lettering is useless if it is "out of sight, out of mind." Integrate your dead-letter storage into your monitoring and alerting pipeline.
- Keep it simple: Avoid complex event chains that become impossible to debug. If a business process requires strict coordination, consider if an orchestration tool might be more appropriate than a series of events.
- Security is a layer, not a checkbox: Always protect your event topics and ensure that only authorized services can publish or subscribe to your data.
- Use filtering wisely: Don't waste resources by sending every event to every service. Use Event Grid’s filtering capabilities to ensure consumers only process what they actually need.
By following these practices, you will be well-equipped to design, implement, and maintain robust event-driven systems in Azure. The transition from simple request-response architectures to event-driven ones is challenging, but the benefits in scalability and resilience are well worth the effort.
FAQ: Frequently Asked Questions
Q: Can I use dead lettering with Event Hubs? A: Event Hubs has a different mechanism called "Capture" and "Dead Lettering" for specific scenarios, but the implementation differs from Event Grid. Event Grid is specifically designed for event routing, while Event Hubs is for high-throughput streaming.
Q: Is there a cost associated with dead lettering? A: Yes, you pay for the storage of the dead-lettered events in your Azure Storage account. However, this cost is generally negligible compared to the cost of losing business data.
Q: How do I know if an event was dead-lettered? A: You can check the "Dead Letter Events" metric in the Azure Portal or use Azure Monitor to create a workbook that visualizes your failed event trends.
Q: Should I delete dead-lettered events after processing? A: Yes. Once you have successfully reprocessed or archived the event, you should delete the blob to keep your storage container clean and to prevent accidental reprocessing of the same event.
Q: Can I change the retry policy for my events? A: You can configure the "Max delivery attempts" and the "Event time to live" in your event subscription settings. This allows you to tune the behavior of your system based on your specific requirements.
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons