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
102
103
104
105
106
107
108
109
110
111
112
113
114
//! # modo::job
//!
//! Durable background job processing backed by SQLite.
//!
//! The `job` module provides a named-job queue stored in the `jobs` SQLite
//! table. Handlers are plain `async fn` functions. The worker polls the
//! database, dispatches jobs to handlers, retries failures with exponential
//! backoff, and reaps stale jobs.
//!
//! ## Capabilities
//!
//! - Named jobs with JSON payloads serialized via `serde`
//! - Per-queue concurrency limits
//! - Automatic retries with exponential backoff
//! - Scheduled execution via [`EnqueueOptions::run_at`]
//! - Idempotent enqueueing via [`Enqueuer::enqueue_unique`]
//! - Stale job reaping (jobs stuck in `running` beyond a configurable threshold)
//! - Job cancellation via [`Enqueuer::cancel`]
//! - Periodic cleanup of terminal jobs
//! - Optional separate SQLite database for job-queue isolation
//!
//! ## Provides
//!
//! **Configuration:**
//!
//! | Type | Purpose |
//! |------|---------|
//! | [`JobConfig`] | Top-level worker configuration (poll interval, queues, cleanup, optional separate DB) |
//! | [`QueueConfig`] | Name and concurrency limit for a single queue |
//! | [`CleanupConfig`] | Interval and retention window for terminal-job cleanup |
//!
//! **Enqueueing:**
//!
//! | Type | Purpose |
//! |------|---------|
//! | [`Enqueuer`] | Inserts and cancels jobs in the `jobs` table |
//! | [`EnqueueOptions`] | Queue name and optional scheduled `run_at` timestamp |
//! | [`EnqueueResult`] | `Created(id)` or `Duplicate(id)` from idempotent enqueue |
//!
//! **Worker:**
//!
//! | Type | Purpose |
//! |------|---------|
//! | [`Worker`] | Running worker handle; implements [`crate::runtime::Task`] for graceful shutdown |
//! | [`WorkerBuilder`] | Fluent builder for registering handlers and starting the worker |
//! | [`JobOptions`] | Per-handler max-attempts and timeout |
//!
//! **Handler system:**
//!
//! | Type | Purpose |
//! |------|---------|
//! | [`JobHandler`] | Trait blanket-implemented for `async fn`s with 0-12 [`FromJobContext`] args |
//! | [`JobContext`] | Runtime context carrying payload, metadata, and service registry |
//! | [`FromJobContext`] | Extraction trait for handler argument types |
//! | [`Payload`] | Handler argument — deserializes the JSON payload into `T` |
//! | [`Meta`] | Handler argument — job ID, name, queue, attempt count, deadline |
//! | [`Status`] | Job lifecycle enum: `Pending`, `Running`, `Completed`, `Dead`, `Cancelled` |
//!
//! ## Quick start
//!
//! ```rust,ignore
//! use modo::job::{Enqueuer, JobConfig, Payload, Meta, Worker};
//! use modo::service::Registry;
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Serialize, Deserialize)]
//! struct WelcomePayload { user_id: String }
//!
//! async fn send_welcome(p: Payload<WelcomePayload>, m: Meta) -> modo::Result<()> {
//! tracing::info!(job_id = %m.id, user_id = %p.user_id, "sending email");
//! Ok(())
//! }
//!
//! // Build and start the worker
//! let worker = Worker::builder(&config, ®istry)
//! .register("send_welcome", send_welcome)
//! .start()
//! .await;
//!
//! // Enqueue a job
//! let enqueuer = Enqueuer::new(db);
//! enqueuer.enqueue("send_welcome", &WelcomePayload {
//! user_id: "usr_01".into(),
//! }).await?;
//! ```
//!
//! ## Database schema
//!
//! The module reads and writes the `jobs` table. End-applications own the
//! migration — no embedded migration is shipped by this module.
//!
//! ## Shutdown
//!
//! [`Worker`] implements [`crate::runtime::Task`] so it integrates with the
//! [`run!`](crate::run) macro for graceful shutdown.
pub use ;
pub use FromJobContext;
pub use JobContext;
pub use ;
pub use JobHandler;
pub use ;
pub use Payload;
pub use ;