A3S Event
Dead Letter Queue
Capture permanently failed events for inspection and debugging
Dead Letter Queue
Events that exceed the maximum delivery count are moved to a dead letter queue (DLQ) for inspection and debugging.
Enable DLQ
use a3s_event::{EventBus, MemoryProvider, MemoryDlqHandler};
use std::sync::Arc;
let mut bus = EventBus::new(MemoryProvider::default());
let dlq = MemoryDlqHandler::new(100); // Max 100 dead letters
bus.set_dlq_handler(Arc::new(dlq));How It Works
When a message has been delivered max_deliver times without a successful ack, the event is routed to the DLQ handler:
Event delivered → processing fails → redelivered → fails again
→ max_deliver reached → DLQ handler receives the eventThe max_deliver threshold is configured via SubscribeOptions:
use a3s_event::SubscribeOptions;
let options = SubscribeOptions {
max_deliver: Some(3), // After 3 failed deliveries, send to DLQ
..Default::default()
};DeadLetterEvent
pub struct DeadLetterEvent {
/// The original received event (includes delivery context)
pub event: ReceivedEvent,
/// Reason the event was sent to DLQ
pub reason: String,
/// Unix timestamp in milliseconds when the event was dead-lettered
pub dead_lettered_at: u64,
/// Original subject the event was published to
pub original_subject: Option<String>,
/// Number of delivery attempts before dead-lettering
pub delivery_attempts: Option<u64>,
/// Unix timestamp in milliseconds of the first delivery failure
pub first_failure_at: Option<u64>,
}Inspecting Dead Letters
let dlq = bus.dlq_handler().unwrap();
// Count dead letters
let count = dlq.count().await?;
// List recent dead letters
let letters = dlq.list(50).await?;
for letter in &letters {
println!("[{}] {}: {}",
letter.dead_lettered_at,
letter.event.event.subject,
letter.reason);
}DlqHandler Trait
Implement custom DLQ backends (e.g., persist to database, forward to alerting):
use a3s_event::{DlqHandler, DeadLetterEvent};
use a3s_event::Result;
use async_trait::async_trait;
#[async_trait]
pub trait DlqHandler: Send + Sync {
/// Handle a dead-lettered event
async fn handle(&self, event: DeadLetterEvent) -> Result<()>;
/// Get the number of events currently in the DLQ
async fn count(&self) -> Result<usize>;
/// List recent dead-lettered events
async fn list(&self, limit: usize) -> Result<Vec<DeadLetterEvent>>;
}Custom Example: Forward to Slack
struct SlackDlqHandler {
webhook_url: String,
memory: MemoryDlqHandler,
}
#[async_trait]
impl DlqHandler for SlackDlqHandler {
async fn handle(&self, dead_letter: DeadLetterEvent) -> Result<()> {
self.memory.handle(dead_letter.clone()).await?;
reqwest::Client::new()
.post(&self.webhook_url)
.json(&serde_json::json!({
"text": format!("DLQ: {} — {}",
dead_letter.event.event.subject,
dead_letter.reason)
}))
.send()
.await
.map_err(|e| EventError::Other(e.to_string()))?;
Ok(())
}
async fn count(&self) -> Result<usize> {
self.memory.count().await
}
async fn list(&self, limit: usize) -> Result<Vec<DeadLetterEvent>> {
self.memory.list(limit).await
}
}MemoryDlqHandler
The built-in in-memory implementation with capacity management:
let dlq = MemoryDlqHandler::new(100);
// When capacity is reached, oldest dead letters are evictedDefault capacity is 1000:
let dlq = MemoryDlqHandler::default();