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.namespace_bytes_max",
118 tier: Tier::Critical,
119 default: || num(crate::storage::cache::DEFAULT_BLOB_NAMESPACE_BYTES_MAX as f64),
120 },
121 ConfigDefault {
122 key: "cache.blob.max_namespaces",
123 tier: Tier::Critical,
124 default: || num(crate::storage::cache::DEFAULT_BLOB_MAX_NAMESPACES as f64),
125 },
126 ConfigDefault {
129 key: "routing.red.advertise_addr",
130 tier: Tier::Optional,
131 default: || text(""),
132 },
133 ConfigDefault {
134 key: "routing.reds.advertise_addr",
135 tier: Tier::Optional,
136 default: || text(""),
137 },
138 ConfigDefault {
139 key: "routing.grpc.advertise_addr",
140 tier: Tier::Optional,
141 default: || text(""),
142 },
143 ConfigDefault {
144 key: "routing.grpcs.advertise_addr",
145 tier: Tier::Optional,
146 default: || text(""),
147 },
148 ConfigDefault {
149 key: "routing.http.advertise_addr",
150 tier: Tier::Optional,
151 default: || text(""),
152 },
153 ConfigDefault {
154 key: "routing.https.advertise_addr",
155 tier: Tier::Optional,
156 default: || text(""),
157 },
158 ConfigDefault {
159 key: "routing.ws.advertise_addr",
160 tier: Tier::Optional,
161 default: || text(""),
162 },
163 ConfigDefault {
164 key: "routing.wss.advertise_addr",
165 tier: Tier::Optional,
166 default: || text(""),
167 },
168 ConfigDefault {
174 key: "storage.binary_document_body",
175 tier: Tier::Optional,
176 default: || JsonValue::Bool(true),
177 },
178 ConfigDefault {
180 key: "durability.mode",
181 tier: Tier::Critical,
182 default: || text("sync"),
183 },
184 ConfigDefault {
189 key: "runtime.result_cache.enabled",
190 tier: Tier::Critical,
191 default: || JsonValue::Bool(true),
192 },
193 ConfigDefault {
196 key: "runtime.result_cache.ttl_seconds",
197 tier: Tier::Optional,
198 default: || num(30.0),
199 },
200 ConfigDefault {
203 key: "runtime.result_cache.capacity_entries",
204 tier: Tier::Optional,
205 default: || num(1000.0),
206 },
207 ConfigDefault {
209 key: "concurrency.locking.enabled",
210 tier: Tier::Critical,
211 default: || JsonValue::Bool(true),
212 },
213 ConfigDefault {
214 key: "concurrency.locking.deadlock_timeout_ms",
215 tier: Tier::Optional,
216 default: || num(5000.0),
217 },
218 ConfigDefault {
220 key: "storage.wal.max_interval_ms",
221 tier: Tier::Critical,
222 default: || num(10.0),
223 },
224 ConfigDefault {
225 key: "storage.wal.min_batch_size",
226 tier: Tier::Optional,
227 default: || num(4.0),
228 },
229 ConfigDefault {
231 key: "storage.deploy.profile",
232 tier: Tier::Critical,
233 default: || text("embedded"),
234 },
235 ConfigDefault {
236 key: "storage.deploy.packaging",
237 tier: Tier::Critical,
238 default: || text("single-file"),
239 },
240 ConfigDefault {
241 key: "storage.deploy.preset",
242 tier: Tier::Critical,
243 default: || text("embedded"),
244 },
245 ConfigDefault {
246 key: "storage.deploy.replica_count",
247 tier: Tier::Critical,
248 default: || num(0.0),
249 },
250 ConfigDefault {
251 key: "storage.deploy.managed_backup",
252 tier: Tier::Critical,
253 default: || JsonValue::Bool(false),
254 },
255 ConfigDefault {
256 key: "storage.deploy.wal_retention",
257 tier: Tier::Critical,
258 default: || JsonValue::Bool(false),
259 },
260 ConfigDefault {
262 key: "storage.bgwriter.delay_ms",
263 tier: Tier::Critical,
264 default: || num(200.0),
265 },
266 ConfigDefault {
267 key: "storage.bgwriter.max_pages_per_round",
268 tier: Tier::Optional,
269 default: || num(100.0),
270 },
271 ConfigDefault {
272 key: "storage.bgwriter.lru_multiplier",
273 tier: Tier::Optional,
274 default: || num(2.0),
275 },
276 ConfigDefault {
278 key: "storage.bulk_insert.max_buffered_rows",
279 tier: Tier::Optional,
280 default: || num(1000.0),
281 },
282 ConfigDefault {
283 key: "storage.bulk_insert.max_buffered_bytes",
284 tier: Tier::Optional,
285 default: || num(65536.0),
286 },
287 ConfigDefault {
289 key: "storage.hot_update.max_chain_hops",
290 tier: Tier::Optional,
291 default: || num(32.0),
292 },
293 ConfigDefault {
295 key: "storage.time_series.max_series_per_collection",
296 tier: Tier::Optional,
297 default: || num(1_000_000.0),
298 },
299 ConfigDefault {
301 key: "storage.btree.lehman_yao",
302 tier: Tier::Critical,
303 default: || JsonValue::Bool(true),
304 },
305 ConfigDefault {
307 key: "ai.collection_descriptions.auto_enrich",
308 tier: Tier::Optional,
309 default: || JsonValue::Bool(false),
310 },
311 ConfigDefault {
314 key: "ai.ner.backend",
315 tier: Tier::Optional,
316 default: || text("heuristic"),
317 },
318 ConfigDefault {
319 key: "ai.ner.endpoint",
320 tier: Tier::Optional,
321 default: || text(""),
322 },
323 ConfigDefault {
324 key: "ai.ner.model",
325 tier: Tier::Optional,
326 default: || text(""),
327 },
328 ConfigDefault {
329 key: "ai.ner.timeout_ms",
330 tier: Tier::Optional,
331 default: || num(5000.0),
332 },
333 ConfigDefault {
334 key: "ai.ner.fallback",
335 tier: Tier::Optional,
336 default: || text("use_heuristic"),
337 },
338 ConfigDefault {
342 key: "runtime.ai.transport_pool_size",
343 tier: Tier::Optional,
344 default: || num(16.0),
345 },
346 ConfigDefault {
347 key: "runtime.ai.transport_timeout_ms",
348 tier: Tier::Optional,
349 default: || num(30000.0),
350 },
351 ConfigDefault {
352 key: "runtime.ai.transport_retry_max_attempts",
353 tier: Tier::Optional,
354 default: || num(3.0),
355 },
356 ConfigDefault {
357 key: "runtime.ai.transport_retry_base_ms",
358 tier: Tier::Optional,
359 default: || num(500.0),
360 },
361 ConfigDefault {
363 key: "cache.blob.policy.extended",
364 tier: Tier::Optional,
365 default: || text("off"),
366 },
367 ConfigDefault {
372 key: "cache.blob.async_promotion",
373 tier: Tier::Optional,
374 default: || text("off"),
375 },
376];
377
378pub fn default_for(key: &str) -> Option<JsonValue> {
382 MATRIX
383 .iter()
384 .find(|entry| entry.key == key)
385 .map(|entry| (entry.default)())
386}
387
388pub fn tier_for(key: &str) -> Option<Tier> {
391 MATRIX
392 .iter()
393 .find(|entry| entry.key == key)
394 .map(|entry| entry.tier)
395}
396
397pub fn heal_critical_keys(store: &UnifiedStore) {
404 for entry in MATRIX {
407 if entry.tier != Tier::Critical {
408 continue;
409 }
410 if is_key_present(store, entry.key) {
411 continue;
412 }
413 store.set_config_tree(entry.key, &(entry.default)());
414 }
415}
416
417fn is_key_present(store: &UnifiedStore, key: &str) -> bool {
420 match store.try_get_config(key) {
421 Ok(value) => value.is_some(),
422 Err(error) => {
423 tracing::warn!(key, %error, "cannot read persisted configuration; skipping seed");
426 true
427 }
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434
435 #[test]
436 fn every_matrix_entry_has_a_default_that_resolves() {
437 for entry in MATRIX {
438 let value = (entry.default)();
439 assert!(
440 !matches!(value, JsonValue::Null),
441 "matrix key {} has a null default, defeats self-heal",
442 entry.key
443 );
444 }
445 }
446
447 #[test]
448 fn critical_keys_cover_the_core_guarantees() {
449 let required_critical = [
452 "cache.blob.l1_bytes_max",
453 "cache.blob.l2_bytes_max",
454 "cache.blob.namespace_bytes_max",
455 "cache.blob.max_namespaces",
456 "durability.mode",
457 "concurrency.locking.enabled",
458 "storage.wal.max_interval_ms",
459 "storage.deploy.profile",
460 "storage.deploy.packaging",
461 "storage.deploy.preset",
462 "storage.deploy.replica_count",
463 "storage.deploy.managed_backup",
464 "storage.deploy.wal_retention",
465 "storage.bgwriter.delay_ms",
466 "storage.btree.lehman_yao",
467 ];
468 for key in required_critical {
469 assert_eq!(
470 tier_for(key),
471 Some(Tier::Critical),
472 "{key} must be a Tier A (Critical) key",
473 );
474 }
475 }
476
477 #[test]
478 fn optional_keys_are_not_self_healed() {
479 let must_be_optional = [
480 "concurrency.locking.deadlock_timeout_ms",
481 "storage.wal.min_batch_size",
482 "storage.bgwriter.max_pages_per_round",
483 "storage.bgwriter.lru_multiplier",
484 "storage.bulk_insert.max_buffered_rows",
485 "storage.bulk_insert.max_buffered_bytes",
486 "storage.hot_update.max_chain_hops",
487 "storage.time_series.max_series_per_collection",
488 ];
489 for key in must_be_optional {
490 assert_eq!(tier_for(key), Some(Tier::Optional), "{key} tier mismatch");
491 }
492 }
493
494 #[test]
495 fn unknown_key_returns_none() {
496 assert!(default_for("nonexistent.key").is_none());
497 assert!(tier_for("nonexistent.key").is_none());
498 }
499}