1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::fmt::Debug;
6use uuid::Uuid;
7
8pub type JobId = Uuid;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13pub enum JobStatus {
14 Pending,
16 Running,
18 Completed,
20 Failed(u32),
22 DeadLetter,
24}
25
26#[async_trait::async_trait]
28pub trait Job: Send + Sync + Debug {
29 fn name(&self) -> &str;
31
32 async fn execute(&mut self) -> JobResult;
34
35 fn max_retries(&self) -> u32 {
37 3
38 }
39
40 fn backoff_strategy(&self) -> BackoffStrategy {
42 BackoffStrategy::Exponential {
43 initial_secs: 1,
44 multiplier: 2.0,
45 }
46 }
47}
48
49#[derive(Debug)]
51pub enum JobResult {
52 Success,
54 Retry(String),
56 Fatal(String),
58}
59
60#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
62pub enum BackoffStrategy {
63 Constant { secs: u64 },
64 Exponential { initial_secs: u64, multiplier: f64 },
65}
66
67impl BackoffStrategy {
68 pub fn delay(&self, attempt: u32) -> std::time::Duration {
69 match self {
70 Self::Constant { secs } => std::time::Duration::from_secs(*secs),
71 Self::Exponential {
72 initial_secs,
73 multiplier,
74 } => {
75 let secs = (*initial_secs as f64 * multiplier.powi(attempt as i32)) as u64;
76 std::time::Duration::from_secs(secs)
77 }
78 }
79 }
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct JobEntry {
85 pub id: JobId,
86 pub tenant_id: String,
87 pub job_type: String,
88 pub payload: serde_json::Value,
89 pub status: JobStatus,
90 pub created_at: DateTime<Utc>,
91 pub run_at: DateTime<Utc>,
92 pub attempts: u32,
93 pub last_error: Option<String>,
94}