1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
8pub enum QueueOrdering {
9 #[default]
11 Fifo,
12 Fastest,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct QueueConfig {
19 pub ordering: QueueOrdering,
20}
21
22impl Default for QueueConfig {
23 fn default() -> Self {
24 Self {
25 ordering: QueueOrdering::Fifo,
26 }
27 }
28}
29
30impl QueueConfig {
31 pub fn new(ordering: QueueOrdering) -> Self {
32 Self { ordering }
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
39pub struct JobListItem {
40 pub id: Uuid,
41 pub queue: String,
42 pub job_type: String,
43 pub status: String,
44
45 pub run_at: DateTime<Utc>,
46 pub priority: i32,
47 pub max_attempts: i32,
48
49 pub last_error_code: Option<String>,
50 pub last_error_message: Option<String>,
51
52 pub dlq_reason_code: Option<String>,
53
54 pub created_at: DateTime<Utc>,
55 pub updated_at: DateTime<Utc>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
76#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
77pub struct Job {
78 pub dataset_id: String,
79 pub replay_of_job_id: Option<Uuid>,
80
81 pub id: Uuid,
82 pub queue: String,
83 pub job_type: String,
84 #[cfg_attr(feature = "sqlx", sqlx(rename = "payload_json"))]
85 pub payload: Value,
86 pub run_at: DateTime<Utc>,
87 pub status: String,
88 pub priority: i32,
89 pub max_attempts: i32,
90
91 pub locked_at: Option<DateTime<Utc>>,
92 pub locked_by: Option<String>,
93 pub lock_expires_at: Option<DateTime<Utc>>,
94
95 pub dlq_reason_code: Option<String>,
96 pub dlq_at: Option<DateTime<Utc>>,
97
98 pub created_at: DateTime<Utc>,
99 pub updated_at: DateTime<Utc>,
100}
101
102impl Job {
103 pub fn new(job_type: impl Into<String>, payload: Value) -> Self {
115 let now = Utc::now();
116 Self {
117 dataset_id: "default".to_string(),
118 replay_of_job_id: None,
119 id: Uuid::new_v4(),
120 queue: "default".to_string(),
121 job_type: job_type.into(),
122 payload,
123 run_at: now,
124 status: JobStatus::Queued.as_str().to_string(),
125 priority: 0,
126 max_attempts: 25,
127 locked_at: None,
128 locked_by: None,
129 lock_expires_at: None,
130 dlq_reason_code: None,
131 dlq_at: None,
132 created_at: now,
133 updated_at: now,
134 }
135 }
136
137 pub fn queue(mut self, queue: impl Into<String>) -> Self {
139 self.queue = queue.into();
140 self
141 }
142
143 pub fn priority(mut self, priority: i32) -> Self {
145 self.priority = priority;
146 self
147 }
148
149 pub fn max_attempts(mut self, max_attempts: i32) -> Self {
151 self.max_attempts = max_attempts;
152 self
153 }
154
155 pub fn run_at(mut self, run_at: DateTime<Utc>) -> Self {
157 self.run_at = run_at;
158 self
159 }
160
161 pub fn payload_json(&self) -> &Value {
163 &self.payload
164 }
165
166 pub fn payload_typed<T: serde::de::DeserializeOwned>(&self) -> Result<T, crate::error::Error> {
184 serde_json::from_value(self.payload.clone())
185 .map_err(crate::error::Error::PayloadDeserialization)
186 }
187}
188
189#[async_trait::async_trait]
191pub trait JobProcessor: Send + Sync {
192 async fn process(&self, job: Job) -> anyhow::Result<()>;
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct NewJob {
199 pub queue: String,
200 pub job_type: String,
201 pub payload_json: Value,
202 pub run_at: DateTime<Utc>,
203 pub priority: i32,
204 pub max_attempts: i32,
205}
206
207impl From<Job> for NewJob {
208 fn from(job: Job) -> Self {
209 NewJob {
210 queue: job.queue,
211 job_type: job.job_type,
212 payload_json: job.payload,
213 run_at: job.run_at,
214 priority: job.priority,
215 max_attempts: job.max_attempts,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
222pub enum JobStatus {
223 Queued,
224 Running,
225 Succeeded,
226 Failed,
227 Dlq,
228 Canceled,
229}
230
231impl JobStatus {
232 pub fn as_str(&self) -> &'static str {
242 match self {
243 JobStatus::Queued => "queued",
244 JobStatus::Running => "running",
245 JobStatus::Succeeded => "succeeded",
246 JobStatus::Failed => "failed",
247 JobStatus::Dlq => "dlq",
248 JobStatus::Canceled => "canceled",
249 }
250 }
251}
252
253pub type JobHandler = std::sync::Arc<
255 dyn Fn(Job) -> std::pin::Pin<Box<dyn std::future::Future<Output = anyhow::Result<()>> + Send>>
256 + Send
257 + Sync,
258>;
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
263pub struct Event {
264 pub sequence_no: i64,
266 pub stream_name: String,
268 pub event_type: String,
270 pub payload_json: serde_json::Value,
272 pub created_at: DateTime<Utc>,
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub struct NewEvent {
279 pub event_type: String,
281 pub payload_json: serde_json::Value,
283}
284
285impl NewEvent {
286 pub fn new(event_type: impl Into<String>, payload_json: serde_json::Value) -> Self {
288 Self {
289 event_type: event_type.into(),
290 payload_json,
291 }
292 }
293}
294
295#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
297#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
298pub struct ConsumerGroupStatus {
299 pub consumer_group: String,
301 pub stream_name: String,
303 pub last_acked_seq: i64,
305 pub updated_at: DateTime<Utc>,
307}