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>>(
55 &self,
56 job: &J,
57 delay: Duration,
58 ) -> Result<uuid::Uuid> {
59 self.enqueue_delayed(job, Some(delay)).await
60 }
61
62 pub async fn defer<J: Job<Queue = Q>>(&self, job: &J, delay: Duration) -> Result<uuid::Uuid> {
77 let mut env = Envelope::new(job)?;
78 env.priority = J::QUEUE.config().max_priority.unwrap_or(0);
79 self.backend.defer(&env, delay).await?;
80 Ok(env.job_id)
81 }
82
83 async fn enqueue_delayed<J: Job<Queue = Q>>(
84 &self,
85 job: &J,
86 delay: Option<Duration>,
87 ) -> Result<uuid::Uuid> {
88 let env = Envelope::new(job)?;
89 self.backend.publish(&env, delay).await?;
90 Ok(env.job_id)
91 }
92
93 pub fn backend(&self) -> &Arc<B> {
95 &self.backend
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::{
103 memory::MemoryBackend,
104 test_support::{Greet, Nudge, TestQueues},
105 };
106
107 async fn producer() -> (Arc<MemoryBackend>, Producer<TestQueues, MemoryBackend>) {
108 let backend = Arc::new(MemoryBackend::new());
109 let producer = Producer::<TestQueues, _>::new(backend.clone())
110 .await
111 .unwrap();
112 (backend, producer)
113 }
114
115 #[tokio::test(start_paused = true)]
116 async fn defer_holds_the_job_and_returns_it_at_the_queue_top_priority() {
117 let (backend, producer) = producer().await;
118 producer.enqueue(&Greet::new("backlog")).await.unwrap();
120
121 let id = producer
122 .defer(&Greet::new("held"), Duration::from_secs(30))
123 .await
124 .unwrap();
125
126 assert_eq!(backend.deferred("test.alpha"), 1);
127 assert_eq!(backend.pending("test.alpha"), 1, "only the backlog so far");
128
129 tokio::time::sleep(Duration::from_secs(29)).await;
130 assert_eq!(backend.pending("test.alpha"), 1);
131
132 tokio::time::sleep(Duration::from_secs(2)).await;
133 assert_eq!(backend.deferred("test.alpha"), 0);
134 assert_eq!(backend.pending("test.alpha"), 2);
135
136 let mut stream = backend.consume(&TestQueues::Alpha.config()).await.unwrap();
137 let first = futures::StreamExt::next(&mut stream)
138 .await
139 .unwrap()
140 .unwrap();
141 let envelope = first.envelope().clone();
142 assert_eq!(envelope.job_id, id, "the deferred job is served first");
143 assert_eq!(envelope.priority, 10);
145 assert_eq!(envelope.deferrals, 0, "the producer never deferred it once");
146 assert_eq!(envelope.attempt, 1);
147 first.ack().await.unwrap();
148 }
149
150 #[tokio::test(start_paused = true)]
151 async fn defer_on_a_queue_without_priorities_uses_zero() {
152 let (backend, producer) = producer().await;
153 producer
154 .defer(&Nudge { id: 1 }, Duration::from_secs(5))
155 .await
156 .unwrap();
157
158 tokio::time::sleep(Duration::from_secs(6)).await;
159 let mut stream = backend.consume(&TestQueues::Gamma.config()).await.unwrap();
160 let delivery = futures::StreamExt::next(&mut stream)
161 .await
162 .unwrap()
163 .unwrap();
164 assert_eq!(delivery.envelope().priority, 0);
165 assert_eq!(delivery.envelope().deferrals, 0);
166 delivery.ack().await.unwrap();
167 }
168
169 #[tokio::test]
170 async fn defer_on_a_closed_backend_fails() {
171 let (backend, producer) = producer().await;
172 backend.close().await.unwrap();
173 assert!(matches!(
174 producer
175 .defer(&Greet::new("x"), Duration::from_secs(1))
176 .await,
177 Err(crate::error::Error::ShutDown)
178 ));
179 }
180}