1use crate::serde_json::Value as JsonValue;
20use crate::storage::UnifiedStore;
21
22#[inline]
23fn num(v: f64) -> JsonValue {
24 JsonValue::Number(v)
25}
26
27#[inline]
28fn text(s: &str) -> JsonValue {
29 JsonValue::String(s.to_string())
30}
31
32#[derive(Debug, Clone)]
35pub struct ConfigDefault {
36 pub key: &'static str,
37 pub tier: Tier,
38 pub default: fn() -> JsonValue,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Tier {
46 Critical,
48 Optional,
50}
51
52pub const MATRIX: &[ConfigDefault] = &[
54 ConfigDefault {
56 key: "ask.max_prompt_tokens",
57 tier: Tier::Optional,
58 default: || num(8192.0),
59 },
60 ConfigDefault {
61 key: "ask.max_completion_tokens",
62 tier: Tier::Optional,
63 default: || num(1024.0),
64 },
65 ConfigDefault {
66 key: "ask.max_sources_bytes",
67 tier: Tier::Optional,
68 default: || num(262_144.0),
69 },
70 ConfigDefault {
71 key: "ask.timeout_ms",
72 tier: Tier::Optional,
73 default: || num(30_000.0),
74 },
75 ConfigDefault {
76 key: "ask.daily_cost_cap_usd",
77 tier: Tier::Optional,
78 default: || text(""),
79 },
80 ConfigDefault {
81 key: "ask.audit.include_answer",
82 tier: Tier::Optional,
83 default: || JsonValue::Bool(false),
84 },
85 ConfigDefault {
86 key: "ask.audit.retention_days",
87 tier: Tier::Optional,
88 default: || num(90.0),
89 },
90 ConfigDefault {
91 key: "ask.cache.enabled",
92 tier: Tier::Optional,
93 default: || JsonValue::Bool(false),
94 },
95 ConfigDefault {
96 key: "ask.cache.default_ttl",
97 tier: Tier::Optional,
98 default: || text(""),
99 },
100 ConfigDefault {
101 key: "ask.cache.max_entries",
102 tier: Tier::Optional,
103 default: || num(1024.0),
104 },
105 ConfigDefault {
107 key: "cache.blob.l1_bytes_max",
108 tier: Tier::Critical,
109 default: || num(crate::storage::cache::DEFAULT_BLOB_L1_BYTES_MAX as f64),
110 },
111 ConfigDefault {
112 key: "cache.blob.l2_bytes_max",
113 tier: Tier::Critical,
114 default: || num(crate::storage::cache::DEFAULT_BLOB_L2_BYTES_MAX as f64),
115 },
116 ConfigDefault {
117 key: "cache.blob.max_namespaces",
118 tier: Tier::Critical,
119 default: || num(crate::storage::cache::DEFAULT_BLOB_MAX_NAMESPACES as f64),
120 },
121 ConfigDefault {
127 key: "storage.binary_document_body",
128 tier: Tier::Optional,
129 default: || JsonValue::Bool(true),
130 },
131 ConfigDefault {
133 key: "durability.mode",
134 tier: Tier::Critical,
135 default: || text("sync"),
136 },
137 ConfigDefault {
139 key: "runtime.result_cache.backend",
140 tier: Tier::Critical,
141 default: || text("legacy"),
142 },
143 ConfigDefault {
147 key: "runtime.result_cache.enabled",
148 tier: Tier::Critical,
149 default: || JsonValue::Bool(true),
150 },
151 ConfigDefault {
154 key: "runtime.result_cache.ttl_seconds",
155 tier: Tier::Optional,
156 default: || num(30.0),
157 },
158 ConfigDefault {
161 key: "runtime.result_cache.capacity_entries",
162 tier: Tier::Optional,
163 default: || num(1000.0),
164 },
165 ConfigDefault {
167 key: "concurrency.locking.enabled",
168 tier: Tier::Critical,
169 default: || JsonValue::Bool(true),
170 },
171 ConfigDefault {
172 key: "concurrency.locking.deadlock_timeout_ms",
173 tier: Tier::Optional,
174 default: || num(5000.0),
175 },
176 ConfigDefault {
178 key: "storage.wal.max_interval_ms",
179 tier: Tier::Critical,
180 default: || num(10.0),
181 },
182 ConfigDefault {
183 key: "storage.wal.min_batch_size",
184 tier: Tier::Optional,
185 default: || num(4.0),
186 },
187 ConfigDefault {
189 key: "storage.deploy.profile",
190 tier: Tier::Critical,
191 default: || text("embedded"),
192 },
193 ConfigDefault {
194 key: "storage.deploy.packaging",
195 tier: Tier::Critical,
196 default: || text("single-file"),
197 },
198 ConfigDefault {
199 key: "storage.deploy.preset",
200 tier: Tier::Critical,
201 default: || text("embedded"),
202 },
203 ConfigDefault {
204 key: "storage.deploy.replica_count",
205 tier: Tier::Critical,
206 default: || num(0.0),
207 },
208 ConfigDefault {
209 key: "storage.deploy.managed_backup",
210 tier: Tier::Critical,
211 default: || JsonValue::Bool(false),
212 },
213 ConfigDefault {
214 key: "storage.deploy.wal_retention",
215 tier: Tier::Critical,
216 default: || JsonValue::Bool(false),
217 },
218 ConfigDefault {
220 key: "storage.bgwriter.delay_ms",
221 tier: Tier::Critical,
222 default: || num(200.0),
223 },
224 ConfigDefault {
225 key: "storage.bgwriter.max_pages_per_round",
226 tier: Tier::Optional,
227 default: || num(100.0),
228 },
229 ConfigDefault {
230 key: "storage.bgwriter.lru_multiplier",
231 tier: Tier::Optional,
232 default: || num(2.0),
233 },
234 ConfigDefault {
236 key: "storage.bulk_insert.max_buffered_rows",
237 tier: Tier::Optional,
238 default: || num(1000.0),
239 },
240 ConfigDefault {
241 key: "storage.bulk_insert.max_buffered_bytes",
242 tier: Tier::Optional,
243 default: || num(65536.0),
244 },
245 ConfigDefault {
247 key: "storage.hot_update.max_chain_hops",
248 tier: Tier::Optional,
249 default: || num(32.0),
250 },
251 ConfigDefault {
253 key: "storage.time_series.max_series_per_collection",
254 tier: Tier::Optional,
255 default: || num(1_000_000.0),
256 },
257 ConfigDefault {
259 key: "storage.btree.lehman_yao",
260 tier: Tier::Critical,
261 default: || JsonValue::Bool(true),
262 },
263 ConfigDefault {
267 key: "ai.ner.backend",
268 tier: Tier::Optional,
269 default: || text("heuristic"),
270 },
271 ConfigDefault {
272 key: "ai.ner.endpoint",
273 tier: Tier::Optional,
274 default: || text(""),
275 },
276 ConfigDefault {
277 key: "ai.ner.model",
278 tier: Tier::Optional,
279 default: || text(""),
280 },
281 ConfigDefault {
282 key: "ai.ner.timeout_ms",
283 tier: Tier::Optional,
284 default: || num(5000.0),
285 },
286 ConfigDefault {
287 key: "ai.ner.fallback",
288 tier: Tier::Optional,
289 default: || text("use_heuristic"),
290 },
291 ConfigDefault {
295 key: "runtime.ai.transport_pool_size",
296 tier: Tier::Optional,
297 default: || num(16.0),
298 },
299 ConfigDefault {
300 key: "runtime.ai.transport_timeout_ms",
301 tier: Tier::Optional,
302 default: || num(30000.0),
303 },
304 ConfigDefault {
305 key: "runtime.ai.transport_retry_max_attempts",
306 tier: Tier::Optional,
307 default: || num(3.0),
308 },
309 ConfigDefault {
310 key: "runtime.ai.transport_retry_base_ms",
311 tier: Tier::Optional,
312 default: || num(500.0),
313 },
314 ConfigDefault {
316 key: "cache.blob.policy.extended",
317 tier: Tier::Optional,
318 default: || text("off"),
319 },
320 ConfigDefault {
325 key: "cache.blob.async_promotion",
326 tier: Tier::Optional,
327 default: || text("off"),
328 },
329];
330
331pub fn default_for(key: &str) -> Option<JsonValue> {
335 MATRIX
336 .iter()
337 .find(|entry| entry.key == key)
338 .map(|entry| (entry.default)())
339}
340
341pub fn tier_for(key: &str) -> Option<Tier> {
344 MATRIX
345 .iter()
346 .find(|entry| entry.key == key)
347 .map(|entry| entry.tier)
348}
349
350pub fn heal_critical_keys(store: &UnifiedStore) {
357 for entry in MATRIX {
360 if entry.tier != Tier::Critical {
361 continue;
362 }
363 if is_key_present(store, entry.key) {
364 continue;
365 }
366 store.set_config_tree(entry.key, &(entry.default)());
367 }
368}
369
370fn is_key_present(store: &UnifiedStore, key: &str) -> bool {
373 match store.try_get_config(key) {
374 Ok(value) => value.is_some(),
375 Err(error) => {
376 tracing::warn!(key, %error, "cannot read persisted configuration; skipping seed");
379 true
380 }
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn every_matrix_entry_has_a_default_that_resolves() {
390 for entry in MATRIX {
391 let value = (entry.default)();
392 assert!(
393 !matches!(value, JsonValue::Null),
394 "matrix key {} has a null default, defeats self-heal",
395 entry.key
396 );
397 }
398 }
399
400 #[test]
401 fn critical_keys_cover_the_core_guarantees() {
402 let required_critical = [
405 "cache.blob.l1_bytes_max",
406 "cache.blob.l2_bytes_max",
407 "cache.blob.max_namespaces",
408 "durability.mode",
409 "runtime.result_cache.backend",
410 "concurrency.locking.enabled",
411 "storage.wal.max_interval_ms",
412 "storage.deploy.profile",
413 "storage.deploy.packaging",
414 "storage.deploy.preset",
415 "storage.deploy.replica_count",
416 "storage.deploy.managed_backup",
417 "storage.deploy.wal_retention",
418 "storage.bgwriter.delay_ms",
419 "storage.btree.lehman_yao",
420 ];
421 for key in required_critical {
422 assert_eq!(
423 tier_for(key),
424 Some(Tier::Critical),
425 "{key} must be a Tier A (Critical) key",
426 );
427 }
428 }
429
430 #[test]
431 fn optional_keys_are_not_self_healed() {
432 let must_be_optional = [
433 "concurrency.locking.deadlock_timeout_ms",
434 "storage.wal.min_batch_size",
435 "storage.bgwriter.max_pages_per_round",
436 "storage.bgwriter.lru_multiplier",
437 "storage.bulk_insert.max_buffered_rows",
438 "storage.bulk_insert.max_buffered_bytes",
439 "storage.hot_update.max_chain_hops",
440 "storage.time_series.max_series_per_collection",
441 ];
442 for key in must_be_optional {
443 assert_eq!(tier_for(key), Some(Tier::Optional), "{key} tier mismatch");
444 }
445 }
446
447 #[test]
448 fn unknown_key_returns_none() {
449 assert!(default_for("nonexistent.key").is_none());
450 assert!(tier_for("nonexistent.key").is_none());
451 }
452}