1pub mod cron;
65pub mod database;
66pub mod fake;
67pub mod job;
68pub mod memory;
69pub mod plugin;
70pub mod queue;
71pub mod schedule;
72pub mod time;
73pub mod worker;
74pub mod chain;
75pub mod progress;
76
77#[cfg(test)]
78mod tests_support;
79
80pub use chain::Chain;
81pub use cron::{Cron, Weekday};
82pub use database::{CreateJobProgressTable, CreateQueueTables, DatabaseProgress, DatabaseQueue};
83pub use fake::{FakeQueue, fake};
84pub use job::{
85 BoxFuture, DEFAULT_QUEUE, DEFAULT_RETRY_AFTER, DEFAULT_TRIES, FailedJob, Job, JobRegistry,
86 QueuedJob, ReservedJob,
87};
88pub use memory::MemoryQueue;
89pub use progress::{JobContext, MemoryProgress, Progress, ProgressStore};
90pub use plugin::QueueDashboard;
91pub use queue::{Queue, QueueExt};
92pub use schedule::{ScheduledEvent, Scheduler, TickReport, schedule};
93pub use worker::{Outcome, Shutdown, Worker, WorkerOptions, WorkerStats, run_pool};
94
95pub use rustlavel_core::{Error, Result};
96
97pub mod prelude {
99 pub use crate::{
100 Cron, DatabaseQueue, FailedJob, FakeQueue, Job, JobRegistry, MemoryQueue, Queue,
101 QueueDashboard, QueueExt, QueuedJob, ReservedJob, Scheduler, Shutdown, Weekday, Worker,
102 run_pool,
103 };
104 pub use rustlavel_core::{Error, Json, Result};
105 pub use std::future::Future;
106 pub use std::sync::Arc;
107 pub use std::time::Duration;
108}
109
110#[cfg(test)]
111mod tests {
112 use crate::prelude::*;
113 use crate::tests_support::{CountingJob, FailingJob, counter, registry};
114 use crate::worker::Outcome;
115
116 #[tokio::test]
119 async fn a_job_travels_from_dispatch_through_a_worker_to_completion() {
120 let queue: Arc<dyn Queue> = Arc::new(MemoryQueue::new());
121 let worker = Worker::new(Arc::clone(&queue), Arc::new(registry()))
122 .poll_interval(Duration::from_millis(5));
123
124 let tally = counter();
125 queue.dispatch(&CountingJob::new(tally, 10)).await.unwrap();
126 queue.dispatch(&CountingJob::new(tally, 5)).await.unwrap();
127 queue.dispatch(&FailingJob::new(counter(), 1)).await.unwrap();
128
129 let shutdown = Shutdown::new();
130 let stats = worker.max_jobs(3).run(shutdown).await.unwrap();
131
132 assert_eq!(stats.processed, 2);
133 assert_eq!(stats.failed, 1);
134 assert_eq!(tally.total(), 15);
135 assert_eq!(queue.size("default").await.unwrap(), 0);
136 assert_eq!(queue.failed_jobs().await.unwrap().len(), 1);
137 }
138
139 #[tokio::test]
142 async fn the_fake_stands_in_for_the_real_thing_in_an_application_test() {
143 let queue = crate::fake();
144 let tally = counter();
145
146 queue.dispatch(&CountingJob::new(tally, 10)).await.unwrap();
147
148 queue.assert_pushed("counting").assert_pushed_times("counting", 1);
149 assert_eq!(tally.runs(), 0);
150 }
151
152 #[tokio::test]
153 async fn a_delayed_job_waits_and_a_worker_finds_nothing_meanwhile() {
154 let queue: Arc<dyn Queue> = Arc::new(MemoryQueue::new());
155 let worker = Worker::new(Arc::clone(&queue), Arc::new(registry()));
156
157 queue
158 .dispatch_later(Duration::from_secs(3600), &CountingJob::new(counter(), 1))
159 .await
160 .unwrap();
161
162 assert_eq!(worker.run_once().await.unwrap(), None);
163 assert_eq!(queue.size("default").await.unwrap(), 1);
164 }
165
166 #[tokio::test]
167 async fn the_scheduler_and_the_worker_meet_in_the_middle() {
168 let memory = Arc::new(MemoryQueue::new());
169 let queue: Arc<dyn Queue> = memory.clone();
170 let tally = counter();
171
172 let mut plan = Scheduler::new(Arc::clone(&queue));
173 plan.job(CountingJob::new(tally, 3)).daily().at("13:00");
174
175 plan.tick(crate::time::to_unix(2026, 8, 29, 13, 0, 0)).await.unwrap();
176
177 let worker = Worker::new(Arc::clone(&queue), Arc::new(registry()))
178 .poll_interval(Duration::from_millis(5))
179 .max_jobs(1);
180
181 let shutdown = Shutdown::new();
184 let signal = shutdown.clone();
185 tokio::spawn(async move {
186 tokio::time::sleep(Duration::from_secs(5)).await;
187 signal.signal();
188 });
189
190 let stats = worker.run(shutdown).await.unwrap();
191
192 assert_eq!(stats.processed, 1);
193 assert_eq!(tally.total(), 3);
194 }
195
196 #[tokio::test]
197 async fn a_worker_survives_whatever_a_job_does_to_it() {
198 use crate::tests_support::PanickingJob;
199
200 let queue: Arc<dyn Queue> = Arc::new(MemoryQueue::new());
201 let worker = Worker::new(Arc::clone(&queue), Arc::new(registry()));
202
203 queue.dispatch(&PanickingJob::new(counter(), 1)).await.unwrap();
204 let healthy = counter();
205 queue.dispatch(&CountingJob::new(healthy, 1)).await.unwrap();
206
207 assert_eq!(worker.run_once().await.unwrap(), Some(Outcome::Failed));
208 assert_eq!(worker.run_once().await.unwrap(), Some(Outcome::Processed));
209 assert_eq!(healthy.runs(), 1);
210 }
211}