use crate::channel::{ChannelOutboundIntent, MessageReceipt};
use async_trait::async_trait;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub const MAX_RETRIES: u32 = 5;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(default, rename_all = "camelCase")]
pub struct QueuedDelivery {
pub id: String,
pub intent: ChannelOutboundIntent,
pub enqueued_at_unix_ms: u64,
pub retry_count: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_attempt_at_unix_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub platform_send_started_at_unix_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recovery_state: Option<DeliveryRecoveryState>,
}
impl QueuedDelivery {
pub fn new(
id: impl Into<String>,
intent: ChannelOutboundIntent,
enqueued_at_unix_ms: u64,
) -> Self {
Self {
id: id.into(),
intent,
enqueued_at_unix_ms,
retry_count: 0,
last_attempt_at_unix_ms: None,
last_error: None,
platform_send_started_at_unix_ms: None,
recovery_state: None,
}
}
}
impl Default for QueuedDelivery {
fn default() -> Self {
Self::new("", ChannelOutboundIntent::default(), 0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum DeliveryRecoveryState {
SendAttemptStarted,
UnknownAfterSend,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeliveryRetryEligibility {
Eligible,
Deferred { remaining_backoff_ms: u64 },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum UnknownSendReconciliation {
Sent {
receipt: MessageReceipt,
#[serde(skip_serializing_if = "Option::is_none")]
message_id: Option<String>,
},
NotSent,
Unresolved {
#[serde(default)]
retryable: bool,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
},
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(default, rename_all = "camelCase")]
pub struct DeliveryAttemptSuccess {
#[serde(skip_serializing_if = "Option::is_none")]
pub receipt: Option<MessageReceipt>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(default, rename_all = "camelCase")]
pub struct DeliveryAttemptFailure {
pub error: String,
pub sent_before_error: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub permanent: Option<bool>,
}
pub type DeliveryAttemptResult =
std::result::Result<DeliveryAttemptSuccess, DeliveryAttemptFailure>;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(default, rename_all = "camelCase")]
pub struct DeliveryRecoverySummary {
pub recovered: u32,
pub failed: u32,
pub skipped_max_retries: u32,
pub deferred_backoff: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ActiveDeliveryClaimResult<T> {
Claimed(T),
ClaimedByOtherOwner,
}
#[derive(Debug, thiserror::Error)]
pub enum DeliveryQueueError {
#[error("delivery queue store error: {0}")]
Store(String),
}
#[async_trait]
pub trait DeliveryQueueStore: Send + Sync {
async fn save_pending(&self, entry: QueuedDelivery) -> Result<(), DeliveryQueueError>;
async fn load_pending(&self, id: &str) -> Result<Option<QueuedDelivery>, DeliveryQueueError>;
async fn load_pending_all(&self) -> Result<Vec<QueuedDelivery>, DeliveryQueueError>;
async fn remove_pending(&self, id: &str) -> Result<(), DeliveryQueueError>;
async fn save_failed(&self, entry: QueuedDelivery) -> Result<(), DeliveryQueueError>;
}
#[async_trait]
pub trait DeliveryQueueHandler: Send + Sync {
async fn deliver(&self, entry: &QueuedDelivery) -> DeliveryAttemptResult;
async fn reconcile_unknown_send(
&self,
_entry: &QueuedDelivery,
) -> Result<Option<UnknownSendReconciliation>, DeliveryQueueError> {
Ok(None)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PendingDeliveryDrainDecision {
pub matches: bool,
pub bypass_backoff: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DurabilityNegotiation {
pub durability: crate::channel::DeliveryDurability,
pub missing_capabilities: Vec<crate::channel::DurableFinalDeliveryCapability>,
}