1use std::{sync::Arc, time::Duration};
4
5use crate::{backend::Backend, envelope::Envelope, error::Result, job::Job, queue::QueueSet};
6
7pub struct Producer<Q: QueueSet, B: Backend> {
10 backend: Arc<B>,
11 _q: std::marker::PhantomData<fn(Q)>,
12}
13
14impl<Q: QueueSet, B: Backend> Clone for Producer<Q, B> {
15 fn clone(&self) -> Self {
16 Self {
17 backend: self.backend.clone(),
18 _q: std::marker::PhantomData,
19 }
20 }
21}
22
23impl<Q: QueueSet, B: Backend> Producer<Q, B> {
24 pub async fn new(backend: Arc<B>) -> Result<Self> {
26 let configs: Vec<_> = Q::all().iter().map(|q| q.config()).collect();
27 backend.declare(&configs).await?;
28 Ok(Self::new_undeclared(backend))
29 }
30
31 pub fn new_undeclared(backend: Arc<B>) -> Self {
33 Self {
34 backend,
35 _q: std::marker::PhantomData,
36 }
37 }
38
39 pub async fn enqueue<J: Job<Queue = Q>>(&self, job: &J) -> Result<uuid::Uuid> {
41 self.enqueue_delayed(job, None).await
42 }
43
44 pub async fn enqueue_after<J: Job<Queue = Q>>(
52 &self,
53 job: &J,
54 delay: Duration,
55 ) -> Result<uuid::Uuid> {
56 self.enqueue_delayed(job, Some(delay)).await
57 }
58
59 pub async fn defer<J: Job<Queue = Q>>(&self, job: &J, delay: Duration) -> Result<uuid::Uuid> {
74 let mut env = Envelope::new(job)?;
75 env.priority = J::QUEUE.config().max_priority.unwrap_or(0);
76 self.backend.defer(&env, delay).await?;
77 Ok(env.job_id)
78 }
79
80 async fn enqueue_delayed<J: Job<Queue = Q>>(
81 &self,
82 job: &J,
83 delay: Option<Duration>,
84 ) -> Result<uuid::Uuid> {
85 let env = Envelope::new(job)?;
86 self.backend.publish(&env, delay).await?;
87 Ok(env.job_id)
88 }
89
90 pub fn backend(&self) -> &Arc<B> {
92 &self.backend
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::{
100 memory::MemoryBackend,
101 test_support::{Greet, Nudge, TestQueues},
102 };
103
104 async fn producer() -> (Arc<MemoryBackend>, Producer<TestQueues, MemoryBackend>) {
105 let backend = Arc::new(MemoryBackend::new());
106 let producer = Producer::<TestQueues, _>::new(backend.clone())
107 .await
108 .unwrap();
109 (backend, producer)
110 }
111
112 #[tokio::test(start_paused = true)]
113 async fn defer_holds_the_job_and_returns_it_at_the_queue_top_priority() {
114 let (backend, producer) = producer().await;
115 producer.enqueue(&Greet::new("backlog")).await.unwrap();
117
118 let id = producer
119 .defer(&Greet::new("held"), Duration::from_secs(30))
120 .await
121 .unwrap();
122
123 assert_eq!(backend.deferred("test.alpha"), 1);
124 assert_eq!(backend.pending("test.alpha"), 1, "only the backlog so far");
125
126 tokio::time::sleep(Duration::from_secs(29)).await;
127 assert_eq!(backend.pending("test.alpha"), 1);
128
129 tokio::time::sleep(Duration::from_secs(2)).await;
130 assert_eq!(backend.deferred("test.alpha"), 0);
131 assert_eq!(backend.pending("test.alpha"), 2);
132
133 let mut stream = backend.consume(&TestQueues::Alpha.config()).await.unwrap();
134 let first = futures::StreamExt::next(&mut stream)
135 .await
136 .unwrap()
137 .unwrap();
138 let envelope = first.envelope().clone();
139 assert_eq!(envelope.job_id, id, "the deferred job is served first");
140 assert_eq!(envelope.priority, 10);
142 assert_eq!(envelope.deferrals, 0, "the producer never deferred it once");
143 assert_eq!(envelope.attempt, 1);
144 first.ack().await.unwrap();
145 }
146
147 #[tokio::test(start_paused = true)]
148 async fn defer_on_a_queue_without_priorities_uses_zero() {
149 let (backend, producer) = producer().await;
150 producer
151 .defer(&Nudge { id: 1 }, Duration::from_secs(5))
152 .await
153 .unwrap();
154
155 tokio::time::sleep(Duration::from_secs(6)).await;
156 let mut stream = backend.consume(&TestQueues::Gamma.config()).await.unwrap();
157 let delivery = futures::StreamExt::next(&mut stream)
158 .await
159 .unwrap()
160 .unwrap();
161 assert_eq!(delivery.envelope().priority, 0);
162 assert_eq!(delivery.envelope().deferrals, 0);
163 delivery.ack().await.unwrap();
164 }
165
166 #[tokio::test]
167 async fn defer_on_a_closed_backend_fails() {
168 let (backend, producer) = producer().await;
169 backend.close().await.unwrap();
170 assert!(matches!(
171 producer
172 .defer(&Greet::new("x"), Duration::from_secs(1))
173 .await,
174 Err(crate::error::Error::ShutDown)
175 ));
176 }
177}