use std::sync::Arc;
use aion_core::ActivityId;
use aion_store::{OutboxRow, OutboxStore};
use tracing::{error, warn};
use crate::error::ServerError;
use crate::worker::OutboxDeliveryCallback;
pub(crate) async fn dead_letter_row(
store: &Arc<dyn OutboxStore>,
failure_callback: Option<&Arc<dyn OutboxDeliveryCallback>>,
row: &OutboxRow,
dispatch_error: &ServerError,
attempted: u32,
max_attempts: u32,
) {
warn!(
dispatch_key = %row.dispatch_key,
attempt = row.attempt,
max_attempts,
error = %dispatch_error,
"outbox dispatch exhausted retry budget; dead-lettering row"
);
if let Err(error) = store.fail_outbox_row(&row.dispatch_key).await {
error!(
dispatch_key = %row.dispatch_key,
%error,
"outbox dispatcher failed to dead-letter row"
);
return;
}
let Some(callback) = failure_callback else {
warn!(
dispatch_key = %row.dispatch_key,
"outbox row dead-lettered without a workflow delivery callback; the workflow was NOT \
told and the row stays redrivable"
);
return;
};
let activity_id = ActivityId::from_sequence_position(row.ordinal);
let reason = format!(
"infrastructure: delivery to worker failed after {attempted} attempts: {dispatch_error}"
);
match callback.deliver_failure(&row.workflow_id, &activity_id, row.run_id.as_ref(), reason) {
Ok(true) => {
warn!(
dispatch_key = %row.dispatch_key,
attempt = row.attempt,
"outbox dead-letter failure delivered to workflow"
);
record_judgment(store, row).await;
}
Ok(false) => {
warn!(
dispatch_key = %row.dispatch_key,
attempt = row.attempt,
"outbox dead-letter failure reached callback but no live workflow accepted it; \
the row remains redrivable"
);
}
Err(error) => {
error!(
dispatch_key = %row.dispatch_key,
%error,
"outbox row dead-lettered but failure delivery to workflow failed; \
the row remains redrivable"
);
}
}
}
async fn record_judgment(store: &Arc<dyn OutboxStore>, row: &OutboxRow) {
match store
.record_outbox_failure_delivered(&row.dispatch_key)
.await
{
Ok(true) => {}
Ok(false) => {
error!(
dispatch_key = %row.dispatch_key,
"outbox dead-letter judgment marker matched no dead-lettered row; the row moved \
underneath the dead-letter path and now looks redrivable despite being judged"
);
}
Err(error) => {
error!(
dispatch_key = %row.dispatch_key,
%error,
"outbox dead-letter failure was delivered but its judgment marker could not be \
recorded; the row now looks redrivable despite being judged"
);
}
}
}