1use std::collections::VecDeque;
9use std::fmt::Debug;
10use std::marker::PhantomData;
11use std::sync::Arc;
12
13use crate::factory::DiscardHandler;
14use crate::factory::DiscardReason;
15use crate::factory::Job;
16use crate::factory::JobKey;
17use crate::Message;
18
19pub trait Queue<TKey, TMsg>: Send + 'static
22where
23 TKey: JobKey,
24 TMsg: Message,
25{
26 fn len(&self) -> usize;
28
29 fn is_empty(&self) -> bool;
31
32 fn pop_front(&mut self) -> Option<Job<TKey, TMsg>>;
34
35 fn discard_oldest(&mut self) -> Option<Job<TKey, TMsg>>;
40
41 fn peek(&self) -> Option<&Job<TKey, TMsg>>;
43
44 fn push_back(&mut self, job: Job<TKey, TMsg>);
46
47 fn remove_expired_items(
54 &mut self,
55 discard_handler: &Option<Arc<dyn DiscardHandler<TKey, TMsg>>>,
56 ) -> usize;
57
58 fn is_job_discardable(&self, _key: &TKey) -> bool {
62 true
63 }
64}
65
66pub trait Priority: Default + From<usize> + Send + 'static {
68 fn get_index(&self) -> usize;
71}
72
73#[derive(strum::FromRepr, Default, Debug, Clone, Copy, Eq, PartialEq, Hash)]
76#[repr(usize)]
77pub enum StandardPriority {
78 Highest = 0,
80 High = 1,
82 Important = 2,
84 #[default]
86 Normal = 3,
87 BestEffort = 4,
89}
90
91#[cfg(feature = "cluster")]
92impl crate::BytesConvertable for StandardPriority {
93 fn from_bytes(bytes: Vec<u8>) -> Self {
94 (u64::from_bytes(bytes) as usize).into()
95 }
96 fn into_bytes(self) -> Vec<u8> {
97 (self as u64).into_bytes()
98 }
99}
100
101impl StandardPriority {
102 pub const fn size() -> usize {
104 5
105 }
106}
107
108impl Priority for StandardPriority {
109 fn get_index(&self) -> usize {
110 *self as usize
111 }
112}
113
114impl From<usize> for StandardPriority {
115 fn from(value: usize) -> Self {
116 Self::from_repr(value).unwrap_or_default()
117 }
118}
119
120pub trait PriorityManager<TKey, TPriority>: Send + Sync + 'static
125where
126 TKey: JobKey,
127 TPriority: Priority,
128{
129 fn is_discardable(&self, job: &TKey) -> bool;
133
134 fn get_priority(&self, job: &TKey) -> Option<TPriority>;
138}
139
140pub struct DefaultQueue<TKey, TMsg>
145where
146 TKey: JobKey,
147 TMsg: Message,
148{
149 q: VecDeque<Job<TKey, TMsg>>,
150}
151
152impl<TKey, TMsg> Debug for DefaultQueue<TKey, TMsg>
153where
154 TKey: JobKey,
155 TMsg: Message,
156{
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 write!(f, "DefaultQueue({} items)", self.q.len())
159 }
160}
161
162impl<TKey, TMsg> Default for DefaultQueue<TKey, TMsg>
163where
164 TKey: JobKey,
165 TMsg: Message,
166{
167 fn default() -> Self {
168 Self { q: VecDeque::new() }
169 }
170}
171
172impl<TKey, TMsg> Queue<TKey, TMsg> for DefaultQueue<TKey, TMsg>
173where
174 TKey: JobKey,
175 TMsg: Message,
176{
177 fn len(&self) -> usize {
179 self.q.len()
180 }
181
182 fn is_empty(&self) -> bool {
184 self.q.is_empty()
185 }
186
187 fn pop_front(&mut self) -> Option<Job<TKey, TMsg>> {
189 self.q.pop_front()
190 }
191
192 fn discard_oldest(&mut self) -> Option<Job<TKey, TMsg>> {
193 self.pop_front()
194 }
195
196 fn peek(&self) -> Option<&Job<TKey, TMsg>> {
197 self.q.front()
198 }
199
200 fn push_back(&mut self, job: Job<TKey, TMsg>) {
202 self.q.push_back(job)
203 }
204
205 fn remove_expired_items(
207 &mut self,
208 discard_handler: &Option<Arc<dyn DiscardHandler<TKey, TMsg>>>,
209 ) -> usize {
210 let before = self.q.len();
211 self.q.retain_mut(|queued_item| {
213 if queued_item.is_expired() {
214 if let Some(handler) = discard_handler {
215 handler.discard(DiscardReason::TtlExpired, queued_item);
216 }
217 false
218 } else {
219 true
220 }
221 });
222 before - self.q.len()
223 }
224}
225
226pub struct PriorityQueue<TKey, TMsg, TPriority, TPriorityManager, const NUM_PRIORITIES: usize>
232where
233 TKey: JobKey,
234 TMsg: Message,
235 TPriority: Priority,
236 TPriorityManager: PriorityManager<TKey, TPriority>,
237{
238 queues: [VecDeque<Job<TKey, TMsg>>; NUM_PRIORITIES],
239 priority_manager: TPriorityManager,
240 _p: PhantomData<fn() -> TPriority>,
241}
242
243impl<TKey, TMsg, TPriority, TPriorityManager, const NUM_PRIORITIES: usize> Debug
244 for PriorityQueue<TKey, TMsg, TPriority, TPriorityManager, NUM_PRIORITIES>
245where
246 TKey: JobKey,
247 TMsg: Message,
248 TPriority: Priority,
249 TPriorityManager: PriorityManager<TKey, TPriority>,
250{
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 write!(f, "PriorityQueue({} items)", self.len())
253 }
254}
255
256impl<TKey, TMsg, TPriority, TPriorityManager, const NUM_PRIORITIES: usize>
257 PriorityQueue<TKey, TMsg, TPriority, TPriorityManager, NUM_PRIORITIES>
258where
259 TKey: JobKey,
260 TMsg: Message,
261 TPriority: Priority,
262 TPriorityManager: PriorityManager<TKey, TPriority>,
263{
264 pub fn new(priority_manager: TPriorityManager) -> Self {
267 Self {
268 _p: PhantomData,
269 priority_manager,
270 queues: [(); NUM_PRIORITIES].map(|_| VecDeque::new()),
271 }
272 }
273}
274
275impl<TKey, TMsg, TPriority, TPriorityManager, const NUM_PRIORITIES: usize> Queue<TKey, TMsg>
276 for PriorityQueue<TKey, TMsg, TPriority, TPriorityManager, NUM_PRIORITIES>
277where
278 TKey: JobKey,
279 TMsg: Message,
280 TPriority: Priority,
281 TPriorityManager: PriorityManager<TKey, TPriority>,
282{
283 fn len(&self) -> usize {
285 self.queues.iter().map(|q| q.len()).sum()
286 }
287
288 fn is_empty(&self) -> bool {
290 self.queues.iter().all(|q| q.is_empty())
291 }
292
293 fn pop_front(&mut self) -> Option<Job<TKey, TMsg>> {
295 for i in 0..NUM_PRIORITIES {
296 if let Some(r) = self.queues[i].pop_front() {
297 return Some(r);
298 }
299 }
300 None
301 }
302
303 fn discard_oldest(&mut self) -> Option<Job<TKey, TMsg>> {
304 for i in (0..NUM_PRIORITIES).rev() {
305 if let Some(r) = self.queues[i].pop_front() {
306 return Some(r);
307 }
308 }
309 None
310 }
311
312 fn peek(&self) -> Option<&Job<TKey, TMsg>> {
313 for i in 0..NUM_PRIORITIES {
314 let maybe = self.queues[i].front();
315 if maybe.is_some() {
316 return maybe;
317 }
318 }
319 None
320 }
321
322 fn push_back(&mut self, job: Job<TKey, TMsg>) {
324 let priority = self
325 .priority_manager
326 .get_priority(&job.key)
327 .unwrap_or_else(Default::default);
328 let idx = priority.get_index();
329 self.queues[idx].push_back(job);
330 }
331
332 fn remove_expired_items(
334 &mut self,
335 discard_handler: &Option<Arc<dyn DiscardHandler<TKey, TMsg>>>,
336 ) -> usize {
337 let mut num_removed = 0;
338
339 for i in 0..NUM_PRIORITIES {
341 self.queues[i].retain_mut(|queued_item| {
342 if queued_item.is_expired() {
343 if let Some(handler) = discard_handler {
344 handler.discard(DiscardReason::TtlExpired, queued_item);
345 }
346 num_removed += 1;
347 false
348 } else {
349 true
350 }
351 });
352 }
353 num_removed
354 }
355
356 fn is_job_discardable(&self, key: &TKey) -> bool {
357 self.priority_manager.is_discardable(key)
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::super::*;
364 use super::*;
365 use crate::concurrency::Duration;
366
367 #[derive(Default, Debug)]
368 enum BasicPriority {
369 #[default]
370 Low,
371 High,
372 }
373
374 impl Priority for BasicPriority {
375 fn get_index(&self) -> usize {
376 match self {
377 BasicPriority::Low => 1,
378 BasicPriority::High => 0,
379 }
380 }
381 }
382
383 impl From<usize> for BasicPriority {
384 fn from(value: usize) -> Self {
385 match value {
386 0 => BasicPriority::High,
387 _ => BasicPriority::Low,
388 }
389 }
390 }
391
392 struct BasicPriorityManager;
393
394 impl PriorityManager<u64, BasicPriority> for BasicPriorityManager {
395 fn get_priority(&self, _key: &u64) -> Option<BasicPriority> {
396 if *_key % 2 == 0 {
397 Some(BasicPriority::High)
398 } else {
399 Some(BasicPriority::Low)
400 }
401 }
402
403 fn is_discardable(&self, _key: &u64) -> bool {
404 false
405 }
406 }
407
408 #[crate::concurrency::test]
409 #[cfg_attr(
410 not(all(target_arch = "wasm32", target_os = "unknown")),
411 tracing_test::traced_test
412 )]
413 async fn test_basic_queueing() {
414 let mut queue = DefaultQueue::<u64, ()>::default();
415 for i in 0..99 {
416 queue.push_back(Job {
417 key: i,
418 accepted: None,
419 msg: (),
420 options: JobOptions::default(),
421 });
422 }
423
424 queue.push_back(Job {
425 key: 99,
426 accepted: None,
427 msg: (),
428 options: JobOptions::new(Some(Duration::from_millis(1))),
429 });
430
431 let oldest = queue.discard_oldest();
432 assert!(matches!(oldest, Some(Job { key: 0, .. })));
433
434 let peeked = queue.peek();
435 assert!(matches!(peeked, Some(Job { key: 1, .. })));
436
437 let popped = queue.pop_front();
438 assert!(matches!(popped, Some(Job { key: 1, .. })));
439
440 let len = queue.len();
441 assert_eq!(len, 98);
442
443 let is_empty = queue.is_empty();
444 assert!(!is_empty);
445
446 crate::concurrency::sleep(Duration::from_millis(2)).await;
447
448 struct MyDiscardHandler;
449
450 impl DiscardHandler<u64, ()> for MyDiscardHandler {
451 fn discard(&self, _reason: DiscardReason, job: &mut Job<u64, ()>) {
452 tracing::info!("discarding job: {}", job.key);
453 assert_eq!(99, job.key);
454 }
455 }
456
457 _ = queue.remove_expired_items(&Some(Arc::new(MyDiscardHandler)));
459 let len = queue.len();
460 assert_eq!(len, 97);
461 }
462
463 #[crate::concurrency::test]
464 #[cfg_attr(
465 not(all(target_arch = "wasm32", target_os = "unknown")),
466 tracing_test::traced_test
467 )]
468 async fn test_priority_queueing() {
469 let mut queue = PriorityQueue::<u64, (), BasicPriority, BasicPriorityManager, 2>::new(
470 BasicPriorityManager,
471 );
472 for i in 0..99 {
473 queue.push_back(Job {
474 key: i,
475 accepted: None,
476 msg: (),
477 options: JobOptions::default(),
478 });
479 }
480
481 queue.push_back(Job {
482 key: 99,
483 accepted: None,
484 msg: (),
485 options: JobOptions::new(Some(Duration::from_millis(1))),
486 });
487
488 let oldest = queue.discard_oldest();
490 assert!(matches!(oldest, Some(Job { key: 1, .. })));
491
492 let peeked = queue.peek();
494 assert!(matches!(peeked, Some(Job { key: 0, .. })));
495
496 let popped = queue.pop_front();
498 assert!(matches!(popped, Some(Job { key: 0, .. })));
499
500 let len = queue.len();
502 assert_eq!(len, 98);
503
504 let is_empty = queue.is_empty();
506 assert!(!is_empty);
507
508 crate::concurrency::sleep(Duration::from_millis(2)).await;
509
510 struct MyDiscardHandler;
511
512 impl DiscardHandler<u64, ()> for MyDiscardHandler {
513 fn discard(&self, _reason: DiscardReason, job: &mut Job<u64, ()>) {
514 tracing::info!("discarding job: {}", job.key);
515 assert_eq!(99, job.key);
516 }
517 }
518
519 _ = queue.remove_expired_items(&Some(Arc::new(MyDiscardHandler)));
521 let len = queue.len();
522 assert_eq!(len, 97);
523 }
524}