Skip to main content

nodedb_types/config/tuning/
config.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Top-level `TuningConfig` aggregating every per-domain tuning struct.
4
5use serde::{Deserialize, Serialize};
6
7use super::bitemporal::BitemporalTuning;
8use super::calvin::CalvinTuning;
9use super::data_plane::{DataPlaneTuning, QueryTuning};
10use super::engines::{GraphTuning, KvTuning, SparseTuning, TimeseriesToning, VectorTuning};
11use super::maintenance::MaintenanceTuning;
12use super::memory::MemoryTuning;
13use super::network::{BridgeTuning, ClusterTransportTuning, NetworkTuning, WalTuning};
14use super::promql::PromqlTuning;
15use super::scheduler::SchedulerTuning;
16use super::shutdown::ShutdownTuning;
17use super::startup::StartupTuning;
18
19/// Top-level tuning configuration.
20///
21/// All fields have sensible defaults derived from the current hardcoded values.
22/// Override via the `[tuning]` section in `config.toml`.
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
24pub struct TuningConfig {
25    #[serde(default)]
26    pub data_plane: DataPlaneTuning,
27    #[serde(default)]
28    pub query: QueryTuning,
29    #[serde(default)]
30    pub vector: VectorTuning,
31    #[serde(default)]
32    pub sparse: SparseTuning,
33    #[serde(default)]
34    pub graph: GraphTuning,
35    #[serde(default)]
36    pub timeseries: TimeseriesToning,
37    #[serde(default)]
38    pub kv: KvTuning,
39    #[serde(default)]
40    pub bridge: BridgeTuning,
41    #[serde(default)]
42    pub network: NetworkTuning,
43    #[serde(default)]
44    pub wal: WalTuning,
45    #[serde(default)]
46    pub cluster_transport: ClusterTransportTuning,
47    #[serde(default)]
48    pub memory: MemoryTuning,
49    #[serde(default)]
50    pub scheduler: SchedulerTuning,
51    #[serde(default)]
52    pub shutdown: ShutdownTuning,
53    #[serde(default)]
54    pub bitemporal: BitemporalTuning,
55    #[serde(default)]
56    pub maintenance: MaintenanceTuning,
57    #[serde(default)]
58    pub calvin: CalvinTuning,
59    #[serde(default)]
60    pub promql: PromqlTuning,
61    #[serde(default)]
62    pub startup: StartupTuning,
63}
64
65impl TuningConfig {
66    /// Tick interval for the bitemporal audit-retention enforcement loop.
67    /// Returns `None` when the operator wants the loop default; returns
68    /// `Some(d)` when `[tuning.bitemporal] tick_interval_secs` is set.
69    pub fn bitemporal_retention_tick(&self) -> Option<std::time::Duration> {
70        Some(self.bitemporal.tick_interval())
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn default_tuning_roundtrip() {
80        let cfg = TuningConfig::default();
81        let toml_str = toml::to_string_pretty(&cfg).expect("serialize");
82        let parsed: TuningConfig = toml::from_str(&toml_str).expect("deserialize");
83
84        assert_eq!(parsed.data_plane.idle_poll_timeout_ms, 100);
85        assert_eq!(parsed.query.sort_run_size, 100_000);
86        assert_eq!(parsed.vector.flat_index_threshold, 10_000);
87        assert_eq!(parsed.sparse.bm25_k1, 1.2);
88        assert_eq!(parsed.graph.max_visited, 100_000);
89        assert_eq!(parsed.graph.varlen_max_results, 100_000);
90        assert_eq!(parsed.graph.varlen_max_frontier, 100_000);
91        assert_eq!(parsed.timeseries.memtable_budget_bytes, 64 * 1024 * 1024);
92        assert_eq!(
93            parsed.timeseries.memtable_hard_limit_bytes,
94            80 * 1024 * 1024
95        );
96        assert_eq!(parsed.timeseries.max_tag_cardinality, 100_000);
97        assert_eq!(parsed.kv.default_capacity, 16_384);
98        assert_eq!(parsed.kv.rehash_load_factor, 0.75);
99        assert_eq!(parsed.kv.expiry_reap_budget, 1024);
100        assert_eq!(parsed.bridge.slab_page_size, 64 * 1024);
101        assert_eq!(parsed.network.default_deadline_secs, 30);
102        assert_eq!(parsed.wal.write_buffer_size, 2 * 1024 * 1024);
103        assert_eq!(parsed.cluster_transport.raft_tick_interval_ms, 10);
104        // New ClusterTransportTuning fields.
105        assert_eq!(
106            parsed.cluster_transport.broadcast_threshold_bytes,
107            8 * 1024 * 1024
108        );
109        assert_eq!(parsed.cluster_transport.ghost_sweep_interval_secs, 1800);
110        assert_eq!(parsed.cluster_transport.health_ping_interval_secs, 5);
111        assert_eq!(parsed.cluster_transport.health_failure_threshold, 3);
112        assert_eq!(parsed.cluster_transport.change_feed_heartbeat_ms, 1_000);
113        // New QueryTuning fields.
114        assert_eq!(parsed.query.doc_cache_entries, 4096);
115        assert_eq!(parsed.query.columnar_flush_threshold, 65_536);
116        assert_eq!(parsed.query.compaction_target_bytes, 256 * 1024 * 1024);
117        assert_eq!(parsed.query.aggregate_chunk_size, 10_000);
118        // New MemoryTuning fields.
119        assert_eq!(parsed.memory.overflow_initial_bytes, 64 * 1024 * 1024);
120        assert_eq!(parsed.memory.overflow_max_bytes, 1024 * 1024 * 1024);
121        assert_eq!(parsed.memory.doc_cache_entries, 4096);
122        assert_eq!(parsed.shutdown.deadline_ms, 900);
123        assert_eq!(parsed.maintenance.auto_analyze_min_mutations, 1000);
124        assert_eq!(parsed.calvin, CalvinTuning::default());
125    }
126
127    #[test]
128    fn calvin_section_overrides_the_scheduler_backlog() {
129        let toml_str = r#"
130[calvin]
131max_inflight_backlog = 256
132"#;
133        let cfg: TuningConfig = toml::from_str(toml_str).expect("deserialize");
134        assert_eq!(cfg.calvin.max_inflight_backlog, 256);
135        assert_eq!(cfg.calvin.channel_capacity, 512);
136    }
137
138    #[test]
139    fn maintenance_section_overrides_the_analyze_floor() {
140        let toml_str = r#"
141[maintenance]
142auto_analyze_min_mutations = 20
143"#;
144        let cfg: TuningConfig = toml::from_str(toml_str).expect("deserialize");
145        assert_eq!(cfg.maintenance.auto_analyze_min_mutations, 20);
146        assert_eq!(cfg.query.sort_run_size, 100_000);
147    }
148
149    #[test]
150    fn partial_override() {
151        let toml_str = r#"
152[query]
153sort_run_size = 50000
154
155[network]
156default_deadline_secs = 60
157"#;
158        let cfg: TuningConfig = toml::from_str(toml_str).expect("deserialize");
159        assert_eq!(cfg.query.sort_run_size, 50_000);
160        assert_eq!(cfg.network.default_deadline_secs, 60);
161        assert_eq!(cfg.vector.seal_threshold, 65_536);
162        // Unset fields retain defaults.
163        assert_eq!(cfg.query.columnar_flush_threshold, 65_536);
164        assert_eq!(cfg.query.compaction_target_bytes, 256 * 1024 * 1024);
165        assert_eq!(cfg.query.aggregate_chunk_size, 10_000);
166    }
167
168    #[test]
169    fn empty_toml_yields_defaults() {
170        let cfg: TuningConfig = toml::from_str("").expect("deserialize");
171        assert_eq!(cfg.data_plane.max_consecutive_panics, 3);
172        assert_eq!(cfg.graph.max_depth, 10);
173        // Memory tuning defaults.
174        assert_eq!(cfg.memory.overflow_initial_bytes, 64 * 1024 * 1024);
175        assert_eq!(cfg.memory.overflow_max_bytes, 1024 * 1024 * 1024);
176        assert_eq!(cfg.memory.doc_cache_entries, 4096);
177        // Cluster transport new defaults.
178        assert_eq!(
179            cfg.cluster_transport.broadcast_threshold_bytes,
180            8 * 1024 * 1024
181        );
182        assert_eq!(cfg.cluster_transport.ghost_sweep_interval_secs, 1800);
183        assert_eq!(cfg.cluster_transport.health_ping_interval_secs, 5);
184        assert_eq!(cfg.cluster_transport.health_failure_threshold, 3);
185    }
186
187    #[test]
188    fn memory_tuning_override() {
189        let toml_str = r#"
190[memory]
191overflow_initial_bytes = 134217728
192overflow_max_bytes = 2147483648
193doc_cache_entries = 8192
194"#;
195        let cfg: TuningConfig = toml::from_str(toml_str).expect("deserialize");
196        assert_eq!(cfg.memory.overflow_initial_bytes, 128 * 1024 * 1024);
197        assert_eq!(cfg.memory.overflow_max_bytes, 2 * 1024 * 1024 * 1024);
198        assert_eq!(cfg.memory.doc_cache_entries, 8192);
199    }
200
201    #[test]
202    fn startup_bounds_are_read_from_the_aggregate() {
203        let cfg: TuningConfig = toml::from_str("").expect("deserialize");
204        assert_eq!(cfg.startup.raft_ready_timeout_ms, 30_000);
205        assert_eq!(cfg.startup.data_group_recovery_timeout_ms, 60_000);
206
207        let toml_str = r#"
208[startup]
209raft_ready_timeout_ms = 1234
210data_group_recovery_timeout_ms = 5678
211"#;
212        let cfg: TuningConfig = toml::from_str(toml_str).expect("deserialize");
213        assert_eq!(cfg.startup.raft_ready_timeout_ms, 1234);
214        assert_eq!(cfg.startup.data_group_recovery_timeout_ms, 5678);
215    }
216}