modo/job/config.rs
1use serde::Deserialize;
2
3/// Top-level configuration for the job worker.
4///
5/// Deserializes from YAML under the `job` key. All fields have defaults so an
6/// empty config block is valid.
7///
8/// # Defaults
9///
10/// | Field | Default |
11/// |---|---|
12/// | `poll_interval_secs` | `1` |
13/// | `stale_threshold_secs` | `600` (10 min) |
14/// | `stale_reaper_interval_secs` | `60` (1 min) |
15/// | `drain_timeout_secs` | `30` |
16/// | `queues` | one `"default"` queue with concurrency 4 |
17/// | `cleanup` | enabled, 1 h interval, 72 h retention |
18#[non_exhaustive]
19#[derive(Debug, Clone, Deserialize)]
20#[serde(default)]
21pub struct JobConfig {
22 /// How often the worker polls the database for new jobs, in seconds.
23 pub poll_interval_secs: u64,
24 /// Jobs stuck in `running` for longer than this many seconds are considered
25 /// stale and reset to `pending` by the reaper.
26 pub stale_threshold_secs: u64,
27 /// How often the stale reaper runs, in seconds.
28 pub stale_reaper_interval_secs: u64,
29 /// Maximum time in seconds to wait for in-flight jobs to finish during
30 /// graceful shutdown.
31 pub drain_timeout_secs: u64,
32 /// Queue definitions. Defaults to a single `"default"` queue.
33 pub queues: Vec<QueueConfig>,
34 /// Optional periodic cleanup of terminal jobs. Set to `None` to disable.
35 pub cleanup: Option<CleanupConfig>,
36 /// Separate SQLite database for the job queue. When set, the job worker
37 /// uses this pool instead of the main application database, keeping
38 /// job-queue writes from contending with app queries.
39 pub database: Option<crate::db::Config>,
40}
41
42impl Default for JobConfig {
43 fn default() -> Self {
44 Self {
45 poll_interval_secs: 1,
46 stale_threshold_secs: 600,
47 stale_reaper_interval_secs: 60,
48 drain_timeout_secs: 30,
49 queues: vec![QueueConfig::default()],
50 cleanup: Some(CleanupConfig::default()),
51 database: None,
52 }
53 }
54}
55
56/// Configuration for a single named queue.
57#[non_exhaustive]
58#[derive(Debug, Clone, Deserialize)]
59pub struct QueueConfig {
60 /// Queue name. Must match the `queue` field used when enqueuing jobs.
61 pub name: String,
62 /// Maximum number of jobs from this queue that run concurrently.
63 /// Defaults to `4`.
64 #[serde(default = "default_concurrency")]
65 pub concurrency: u32,
66}
67
68fn default_concurrency() -> u32 {
69 4
70}
71
72impl Default for QueueConfig {
73 fn default() -> Self {
74 Self {
75 name: "default".to_string(),
76 concurrency: 4,
77 }
78 }
79}
80
81/// Configuration for the periodic cleanup of terminal jobs.
82///
83/// Terminal jobs (status `completed`, `dead`, or `cancelled`) whose
84/// `updated_at` is older than `retention_secs` are deleted from the database.
85#[non_exhaustive]
86#[derive(Debug, Clone, Deserialize)]
87#[serde(default)]
88pub struct CleanupConfig {
89 /// How often the cleanup task runs, in seconds. Defaults to `3600` (1 h).
90 pub interval_secs: u64,
91 /// Jobs whose `updated_at` is older than this many seconds are deleted.
92 /// Defaults to `259200` (72 h).
93 pub retention_secs: u64,
94}
95
96impl Default for CleanupConfig {
97 fn default() -> Self {
98 Self {
99 interval_secs: 3600,
100 retention_secs: 259_200, // 72h
101 }
102 }
103}