Skip to main content

praxis_core/server/
runtime.rs

1// SPDX-License-Identifier: LGPL-3.0-only
2// Copyright (c) 2024 Shane Utt
3
4//! Runtime options passed from config into the server factory.
5
6// -----------------------------------------------------------------------------
7// RuntimeOptions
8// -----------------------------------------------------------------------------
9
10/// Runtime tuning passed from config into the server factory.
11///
12/// ```
13/// use praxis_core::server::RuntimeOptions;
14///
15/// let opts = RuntimeOptions::default();
16/// assert_eq!(opts.threads, 0);
17/// assert!(opts.work_stealing);
18/// assert_eq!(opts.global_queue_interval, Some(61));
19/// assert!(opts.upstream_ca_file.is_none());
20/// assert!(opts.upstream_keepalive_pool_size.is_none());
21///
22/// let opts = RuntimeOptions {
23///     threads: 4,
24///     work_stealing: true,
25///     ..RuntimeOptions::default()
26/// };
27/// assert_eq!(opts.threads, 4);
28/// ```
29#[derive(Debug, Clone)]
30pub struct RuntimeOptions {
31    /// Worker threads per service. `0` means auto-detect.
32    pub threads: usize,
33
34    /// Allow work-stealing between threads.
35    pub work_stealing: bool,
36
37    /// Fixed global queue interval for the tokio scheduler.
38    pub global_queue_interval: Option<u32>,
39
40    /// PEM CA file for all upstream TLS connections. Replaces the
41    /// system trust store when set (not additive).
42    pub upstream_ca_file: Option<String>,
43
44    /// Per-thread upstream keepalive pool size. `None` uses
45    /// Pingora's default (128).
46    pub upstream_keepalive_pool_size: Option<usize>,
47}
48
49impl Default for RuntimeOptions {
50    fn default() -> Self {
51        Self {
52            threads: 0,
53            work_stealing: true,
54            global_queue_interval: Some(61),
55            upstream_ca_file: None,
56            upstream_keepalive_pool_size: None,
57        }
58    }
59}
60
61impl From<&crate::config::RuntimeConfig> for RuntimeOptions {
62    fn from(cfg: &crate::config::RuntimeConfig) -> Self {
63        Self {
64            threads: cfg.threads,
65            work_stealing: cfg.work_stealing,
66            global_queue_interval: cfg.global_queue_interval,
67            upstream_ca_file: cfg.upstream_ca_file.clone(),
68            upstream_keepalive_pool_size: cfg.upstream_keepalive_pool_size,
69        }
70    }
71}
72
73// -----------------------------------------------------------------------------
74// Tests
75// -----------------------------------------------------------------------------
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn default_has_zero_threads_and_work_stealing_true() {
83        let opts = RuntimeOptions::default();
84        assert_eq!(opts.threads, 0, "default threads should be 0 (auto-detect)");
85        assert!(opts.work_stealing, "work_stealing should default to true");
86        assert_eq!(
87            opts.global_queue_interval,
88            Some(61),
89            "default global_queue_interval should be 61"
90        );
91    }
92
93    #[test]
94    fn from_runtime_config_copies_all_fields() {
95        let cfg = crate::config::RuntimeConfig {
96            threads: 8,
97            work_stealing: false,
98            global_queue_interval: Some(128),
99            upstream_ca_file: Some("/etc/ssl/ca.pem".to_owned()),
100            upstream_keepalive_pool_size: Some(32),
101            ..crate::config::RuntimeConfig::default()
102        };
103        let opts = RuntimeOptions::from(&cfg);
104        assert_eq!(opts.threads, 8, "threads should match config");
105        assert!(!opts.work_stealing, "work_stealing should match config");
106        assert_eq!(opts.global_queue_interval, Some(128), "interval should match config");
107        assert_eq!(
108            opts.upstream_ca_file.as_deref(),
109            Some("/etc/ssl/ca.pem"),
110            "upstream_ca_file should match config"
111        );
112        assert_eq!(
113            opts.upstream_keepalive_pool_size,
114            Some(32),
115            "pool size should match config"
116        );
117    }
118
119    #[test]
120    fn explicit_fields_are_preserved() {
121        let opts = RuntimeOptions {
122            threads: 4,
123            work_stealing: false,
124            global_queue_interval: Some(128),
125            upstream_ca_file: Some("/ca.pem".to_owned()),
126            upstream_keepalive_pool_size: Some(32),
127        };
128        assert_eq!(opts.threads, 4, "explicit threads should be preserved");
129        assert!(!opts.work_stealing, "explicit work_stealing=false should be preserved");
130        assert_eq!(
131            opts.global_queue_interval,
132            Some(128),
133            "explicit interval should be preserved"
134        );
135        assert_eq!(
136            opts.upstream_ca_file.as_deref(),
137            Some("/ca.pem"),
138            "explicit upstream_ca_file should be preserved"
139        );
140        assert_eq!(
141            opts.upstream_keepalive_pool_size,
142            Some(32),
143            "explicit pool size should be preserved"
144        );
145    }
146}