1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! # rainier-queue
//!
//! Deferred work: the [`Job`] contract, the [`Queue`] port and its drivers, and
//! the [`Worker`] that runs them.
//!
//! ```
//! use rainier_queue::{Job, JobContext, JobRegistry, MemoryQueue, Queue, QueueManager};
//! use serde::{Deserialize, Serialize};
//! use std::sync::Arc;
//!
//! #[derive(Serialize, Deserialize)]
//! struct SendWelcomeEmail { user_id: u64 }
//!
//! #[async_trait::async_trait]
//! impl Job for SendWelcomeEmail {
//! const NAME: &'static str = "mail.welcome";
//! const QUEUE: &'static str = "mail";
//!
//! async fn handle(&self, _context: &JobContext) -> rainier_support::Result<()> {
//! Ok(())
//! }
//! }
//!
//! # #[tokio::main] async fn main() -> rainier_support::Result<()> {
//! let registry = Arc::new(JobRegistry::new().with::<SendWelcomeEmail>());
//! let queue = Arc::new(MemoryQueue::new());
//! let dispatcher = QueueManager::new(queue.clone(), registry);
//!
//! dispatcher.dispatch(SendWelcomeEmail { user_id: 1 }).await?;
//! assert_eq!(queue.size("mail").await?, 1);
//! # Ok(()) }
//! ```
//!
//! ## A job crosses a process boundary
//!
//! Everything about the design follows from that. A job is written by a web
//! request and read, later, by a worker that may be a different process on a
//! different machine — so a job is a **serialisable payload plus a stable
//! name**, not a closure, and the worker needs a [`JobRegistry`] to turn the
//! name back into code. Dependencies cannot be captured either, so a running
//! job resolves them from the container through its [`JobContext`].
//!
//! ## Drivers
//!
//! | Driver | Survives a restart | For |
//! |---|---|---|
//! | [`SyncQueue`] | — runs inline | development, and tests that want the side effect |
//! | [`MemoryQueue`] | no | tests, single-process development |
//! | [`DatabaseQueue`] | yes | production, on the database you already have |
//! | `SqsQueue` | yes | production, managed — needs the `sqs` feature |
//!
//! ## Testing
//!
//! [`QueueManager::fake`] records dispatches instead of performing them:
//!
//! ```
//! # use rainier_queue::{Job, JobContext, QueueManager};
//! # use serde::{Deserialize, Serialize};
//! # #[derive(Serialize, Deserialize)] struct SendInvoice;
//! # #[async_trait::async_trait] impl Job for SendInvoice {
//! # const NAME: &'static str = "billing.invoice";
//! # async fn handle(&self, _: &JobContext) -> rainier_support::Result<()> { Ok(()) }
//! # }
//! # #[tokio::main] async fn main() -> rainier_support::Result<()> {
//! let queue = QueueManager::fake();
//! queue.dispatch(SendInvoice).await?;
//! queue.assert_pushed_times::<SendInvoice>(1);
//! # Ok(()) }
//! ```
pub use ;
pub use QueueDriver;
pub use ;
pub use ;
pub use ;
pub use ;
pub use SqsQueue;
pub use ;
// Re-exported so job implementations get the attribute macro without adding
// the dependency themselves.
pub use async_trait;