///|
/// Retry and dead-letter helpers for failed dispatch flows.
pub(all) enum RetryDecision {
Retry(Event)
DeadLetter(Event)
Drop(String)
} derive(Eq, Debug)
///|
pub(all) struct RetryPolicy {
max_attempts : Int
dead_letter_topic : String
retry_tag : String
} derive(Eq, Debug)
///|
pub fn RetryPolicy::new(
max_attempts? : Int = 3,
dead_letter_topic? : String = "dead-letter",
retry_tag? : String = "retry",
) -> RetryPolicy {
{ max_attempts, dead_letter_topic, retry_tag }
}
///|
pub fn RetryPolicy::decide(
self : RetryPolicy,
event : Event,
attempt : Int,
reason : String,
) -> RetryDecision {
if attempt < self.max_attempts {
Retry({
name: event.name,
topic: event.topic,
payload: event.payload + "|retry=" + attempt.to_string(),
tags: append_tag(event.tags, self.retry_tag),
priority: event.priority,
correlation_id: event.correlation_id,
})
} else if self.dead_letter_topic == "" {
Drop(reason)
} else {
DeadLetter({
name: "dead." + event.name,
topic: self.dead_letter_topic,
payload: event.payload + "|reason=" + reason,
tags: append_tag(event.tags, "dead-letter"),
priority: event.priority,
correlation_id: event.correlation_id,
})
}
}
///|
pub fn append_tag(tags : Array[String], tag : String) -> Array[String] {
let next : Array[String] = []
for existing in tags {
next.push(existing)
}
if !next.contains(tag) {
next.push(tag)
}
next
}
///|
pub fn report_needs_retry(report : DispatchReport) -> Bool {
report.failed > 0
}
///|
pub fn collect_failures(report : DispatchReport) -> Array[String] {
let reasons : Array[String] = []
for item in report.reports {
match item.status {
Failed(reason) => reasons.push(reason)
_ => ()
}
}
reasons
}
///|
pub fn dead_letter_queue(
reports : Array[DispatchReport],
policy : RetryPolicy,
) -> EventQueue {
let mut queue = EventQueue::new()
for report in reports {
if report_needs_retry(report) {
match
policy.decide(report.event, policy.max_attempts, "dispatch failed") {
DeadLetter(event) => queue = queue.push(event)
_ => ()
}
}
}
queue
}