1use crate::task::{Task, TaskId};
5use crate::{TaskQueue, TaskQueueError, TaskSerializer};
6use async_trait::async_trait;
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9use std::collections::{HashMap, VecDeque};
10use std::marker::PhantomData;
11use std::sync::Mutex;
12
13pub struct InMemoryTaskQueue<D, S>
14where
15 S: TaskSerializer,
16{
17 pub hashmap: Mutex<HashMap<TaskId, Vec<u8>>>,
18 pub list: Mutex<VecDeque<TaskId>>,
19 pub inflight: Mutex<HashMap<TaskId, ()>>,
20 pub dlq: Mutex<HashMap<TaskId, Vec<u8>>>,
21 _marker: PhantomData<(D, S)>,
22}
23
24impl<D, S> InMemoryTaskQueue<D, S>
25where
26 S: TaskSerializer,
27{
28 pub fn new() -> Self {
29 Self {
30 hashmap: Mutex::new(HashMap::new()),
31 list: Mutex::new(VecDeque::new()),
32 inflight: Mutex::new(HashMap::new()),
33 dlq: Mutex::new(HashMap::new()),
34 _marker: PhantomData,
35 }
36 }
37}
38
39impl<D, S> Default for InMemoryTaskQueue<D, S>
40where
41 S: TaskSerializer,
42{
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48#[async_trait]
49impl<D, S> TaskQueue<D> for InMemoryTaskQueue<D, S>
50where
51 D: std::fmt::Debug
52 + Clone
53 + Serialize
54 + DeserializeOwned
55 + Send
56 + Sync
57 + 'static,
58 S: TaskSerializer + Send + Sync,
59{
60 async fn push(&self, task: &Task<D>) -> Result<(), TaskQueueError> {
61 let mut list = self
62 .list
63 .lock()
64 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
65 let mut hashmap = self
66 .hashmap
67 .lock()
68 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
69 let mut inflight = self
70 .inflight
71 .lock()
72 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
73
74 let task_bytes = S::serialize_task(task)?;
75 hashmap.insert(task.task_id, task_bytes);
76 list.push_back(task.task_id);
77 inflight.remove(&task.task_id);
78 Ok(())
79 }
80
81 async fn pop(&self) -> Result<Task<D>, TaskQueueError> {
82 let mut list = self
83 .list
84 .lock()
85 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
86 let hashmap = self
87 .hashmap
88 .lock()
89 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
90 let mut inflight = self
91 .inflight
92 .lock()
93 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
94
95 if let Some(task_id) = list.pop_front() {
96 let task_bytes = hashmap
97 .get(&task_id)
98 .ok_or(TaskQueueError::TaskNotFound(task_id))?;
99 inflight.insert(task_id, ());
100 S::deserialize_task(task_bytes)
101 } else {
102 Err(TaskQueueError::QueueEmpty)
103 }
104 }
105
106 async fn ack(&self, task_id: &TaskId) -> Result<(), TaskQueueError> {
107 let mut hashmap = self
108 .hashmap
109 .lock()
110 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
111 let mut inflight = self
112 .inflight
113 .lock()
114 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
115 hashmap
116 .remove(task_id)
117 .ok_or(TaskQueueError::TaskNotFound(*task_id))?;
118 inflight.remove(task_id);
119 Ok(())
120 }
121
122 async fn nack(&self, task: &Task<D>) -> Result<(), TaskQueueError> {
123 let mut dlq = self
124 .dlq
125 .lock()
126 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
127 let task_bytes = S::serialize_task(task)?;
128 dlq.insert(task.task_id, task_bytes);
129
130 let mut hashmap = self
131 .hashmap
132 .lock()
133 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
134 let mut inflight = self
135 .inflight
136 .lock()
137 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
138 hashmap
139 .remove(&task.task_id)
140 .ok_or(TaskQueueError::TaskNotFound(task.task_id))?;
141 inflight.remove(&task.task_id);
142 Ok(())
143 }
144
145 async fn set(&self, task: &Task<D>) -> Result<(), TaskQueueError> {
146 let mut hashmap = self
147 .hashmap
148 .lock()
149 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
150 let task_bytes = S::serialize_task(task)?;
151 hashmap.insert(task.task_id, task_bytes);
152 Ok(())
153 }
154
155 async fn recover_inflight(&self) -> Result<u64, TaskQueueError> {
156 let mut list = self
157 .list
158 .lock()
159 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
160 let mut inflight = self
161 .inflight
162 .lock()
163 .map_err(|e| TaskQueueError::QueueError(e.to_string()))?;
164 let recovered = inflight.len() as u64;
165 for task_id in inflight.drain().map(|(id, _)| id) {
166 list.push_back(task_id);
167 }
168 Ok(recovered)
169 }
170}
171
172impl<D, S> std::fmt::Debug for InMemoryTaskQueue<D, S>
173where
174 S: TaskSerializer,
175{
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 let hashmap = self.hashmap.lock().unwrap();
178 let list = self.list.lock().unwrap();
179 let inflight = self.inflight.lock().unwrap();
180 let dlq = self.dlq.lock().unwrap();
181
182 f.debug_struct("InMemoryTaskQueue")
183 .field("hashmap_size", &hashmap.len())
184 .field("list", &*list)
185 .field("inflight_size", &inflight.len())
186 .field("dlq_size", &dlq.len())
187 .finish()
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::JsonSerializer;
195 use serde::{Deserialize, Serialize};
196 use tokio;
197
198 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
199 struct TestData {
200 value: u32,
201 }
202
203 #[tokio::test]
204 async fn test_push_and_pop() {
205 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
206 let task = Task::new(TestData { value: 42 });
207
208 queue.push(&task).await.unwrap();
209 let popped_task = queue.pop().await.unwrap();
210
211 assert_eq!(popped_task.payload, TestData { value: 42 });
212 }
213
214 #[tokio::test]
215 async fn test_queue_empty() {
216 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
217
218 match queue.pop().await {
219 Err(TaskQueueError::QueueEmpty) => (),
220 _ => panic!("Expected QueueEmpty error"),
221 }
222 }
223
224 #[tokio::test]
225 async fn test_ack() {
226 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
227 let task = Task::new(TestData { value: 42 });
228
229 queue.push(&task).await.unwrap();
230 let popped_task = queue.pop().await.unwrap();
231 queue.ack(&popped_task.task_id).await.unwrap();
232
233 assert!(matches!(queue.pop().await, Err(TaskQueueError::QueueEmpty)));
235 }
236
237 #[tokio::test]
238 async fn test_nack() {
239 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
240 let task = Task::new(TestData { value: 42 });
241
242 queue.push(&task).await.unwrap();
243 let popped_task = queue.pop().await.unwrap();
244 queue.nack(&popped_task).await.unwrap();
245
246 assert!(matches!(queue.pop().await, Err(TaskQueueError::QueueEmpty)));
248
249 let dlq = queue.dlq.lock().unwrap();
251 assert_eq!(dlq.len(), 1);
252 }
253
254 #[tokio::test]
255 async fn test_set() {
256 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
257 let mut task = Task::new(TestData { value: 42 });
258
259 queue.push(&task).await.unwrap();
260
261 task.payload.value = 43;
263 queue.set(&task).await.unwrap();
264
265 let updated_task = queue.pop().await.unwrap();
266 assert_eq!(updated_task.payload, TestData { value: 43 });
267 }
268
269 #[tokio::test]
270 async fn test_multiple_tasks() {
271 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
272 let tasks = vec![
273 Task::new(TestData { value: 1 }),
274 Task::new(TestData { value: 2 }),
275 Task::new(TestData { value: 3 }),
276 ];
277
278 for task in &tasks {
279 queue.push(task).await.unwrap();
280 }
281
282 for expected_task in tasks {
283 let popped_task = queue.pop().await.unwrap();
284 assert_eq!(popped_task.payload, expected_task.payload);
285 }
286
287 assert!(matches!(queue.pop().await, Err(TaskQueueError::QueueEmpty)));
289 }
290
291 #[tokio::test]
292 async fn test_task_not_found() {
293 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
294 let non_existent_task_id = TaskId::new();
295
296 match queue.ack(&non_existent_task_id).await {
297 Err(TaskQueueError::TaskNotFound(_)) => (),
298 _ => panic!("Expected TaskNotFound error"),
299 }
300 }
301
302 #[tokio::test]
303 async fn pop_moves_to_inflight() {
304 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
305 let task = Task::new(TestData { value: 1 });
306
307 queue.push(&task).await.unwrap();
308 let popped = queue.pop().await.unwrap();
309
310 assert!(queue.inflight.lock().unwrap().contains_key(&popped.task_id));
311 }
312
313 #[tokio::test]
314 async fn ack_clears_inflight() {
315 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
316 let task = Task::new(TestData { value: 2 });
317
318 queue.push(&task).await.unwrap();
319 let popped = queue.pop().await.unwrap();
320 queue.ack(&popped.task_id).await.unwrap();
321
322 assert!(queue.inflight.lock().unwrap().is_empty());
323 }
324
325 #[tokio::test]
326 async fn nack_clears_inflight() {
327 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
328 let task = Task::new(TestData { value: 3 });
329
330 queue.push(&task).await.unwrap();
331 let popped = queue.pop().await.unwrap();
332 queue.nack(&popped).await.unwrap();
333
334 assert!(queue.inflight.lock().unwrap().is_empty());
335 assert_eq!(queue.dlq.lock().unwrap().len(), 1);
336 }
337
338 #[tokio::test]
339 async fn push_retry_clears_inflight() {
340 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
341 let mut task = Task::new(TestData { value: 4 });
342
343 queue.push(&task).await.unwrap();
344 let popped = queue.pop().await.unwrap();
345 assert!(queue.inflight.lock().unwrap().contains_key(&popped.task_id));
346
347 task.set_retry("retry");
348 queue.push(&task).await.unwrap();
349
350 assert!(queue.inflight.lock().unwrap().is_empty());
351 assert_eq!(queue.list.lock().unwrap().len(), 1);
352 }
353
354 #[tokio::test]
355 async fn recover_inflight_moves_back_to_queue() {
356 let queue = InMemoryTaskQueue::<TestData, JsonSerializer>::new();
357 let task = Task::new(TestData { value: 5 });
358
359 queue.push(&task).await.unwrap();
360 let popped = queue.pop().await.unwrap();
361 assert!(queue.inflight.lock().unwrap().contains_key(&popped.task_id));
362
363 let recovered = queue.recover_inflight().await.unwrap();
364 assert_eq!(recovered, 1);
365 assert!(queue.inflight.lock().unwrap().is_empty());
366
367 let re_popped = queue.pop().await.unwrap();
368 assert_eq!(re_popped.task_id, popped.task_id);
369 }
370}