Skip to main content

reifydb_core/interface/catalog/
config.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::{fmt, str::FromStr, time::Duration as StdDuration};
5
6use reifydb_type::value::{Value, duration::Duration, r#type::Type};
7
8use crate::common::CommitVersion;
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub enum ConfigKey {
12	OracleWindowSize,
13	OracleWaterMark,
14	RowTtlScanBatchSize,
15	RowTtlScanInterval,
16}
17
18impl ConfigKey {
19	pub fn all() -> &'static [Self] {
20		&[Self::OracleWindowSize, Self::OracleWaterMark, Self::RowTtlScanBatchSize, Self::RowTtlScanInterval]
21	}
22
23	pub fn default_value(&self) -> Value {
24		match self {
25			Self::OracleWindowSize => Value::Uint8(500),
26			Self::OracleWaterMark => Value::Uint8(20),
27			Self::RowTtlScanBatchSize => Value::Uint8(10000),
28			Self::RowTtlScanInterval => Value::Duration(Duration::from_seconds(60).unwrap()),
29		}
30	}
31
32	pub fn description(&self) -> &'static str {
33		match self {
34			Self::OracleWindowSize => "Number of transactions per conflict-detection window.",
35			Self::OracleWaterMark => "Number of conflict windows retained before cleanup is triggered.",
36			Self::RowTtlScanBatchSize => "Max rows to examine per batch during a row TTL scan.",
37			Self::RowTtlScanInterval => "How often the row TTL actor should scan for expired rows.",
38		}
39	}
40
41	pub fn requires_restart(&self) -> bool {
42		match self {
43			Self::OracleWindowSize => false,
44			Self::OracleWaterMark => false,
45			Self::RowTtlScanBatchSize => false,
46			Self::RowTtlScanInterval => false,
47		}
48	}
49
50	pub fn expected_types(&self) -> &'static [Type] {
51		match self {
52			Self::OracleWindowSize => &[Type::Uint8],
53			Self::OracleWaterMark => &[Type::Uint8],
54			Self::RowTtlScanBatchSize => &[Type::Uint8],
55			Self::RowTtlScanInterval => &[Type::Duration],
56		}
57	}
58}
59
60impl fmt::Display for ConfigKey {
61	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62		match self {
63			Self::OracleWindowSize => write!(f, "ORACLE_WINDOW_SIZE"),
64			Self::OracleWaterMark => write!(f, "ORACLE_WATER_MARK"),
65			Self::RowTtlScanBatchSize => write!(f, "ROW_TTL_SCAN_BATCH_SIZE"),
66			Self::RowTtlScanInterval => write!(f, "ROW_TTL_SCAN_INTERVAL"),
67		}
68	}
69}
70
71impl FromStr for ConfigKey {
72	type Err = String;
73
74	fn from_str(s: &str) -> Result<Self, Self::Err> {
75		match s {
76			"ORACLE_WINDOW_SIZE" => Ok(Self::OracleWindowSize),
77			"ORACLE_WATER_MARK" => Ok(Self::OracleWaterMark),
78			"ROW_TTL_SCAN_BATCH_SIZE" => Ok(Self::RowTtlScanBatchSize),
79			"ROW_TTL_SCAN_INTERVAL" => Ok(Self::RowTtlScanInterval),
80			_ => Err(format!("Unknown system configuration key: {}", s)),
81		}
82	}
83}
84
85/// A configuration definition for a runtime-tunable database setting.
86///
87/// `value` is the currently active value (either the persisted override or the default).
88/// `default_value`, `description`, and `requires_restart` are compile-time constants
89/// provided at registration time — they are never stored to disk.
90#[derive(Debug, Clone)]
91pub struct Config {
92	/// System configuration key
93	pub key: ConfigKey,
94	/// Currently active value (persisted override or default)
95	pub value: Value,
96	/// Compile-time default value
97	pub default_value: Value,
98	/// Human-readable description
99	pub description: &'static str,
100	/// Whether changing this setting requires a database restart
101	pub requires_restart: bool,
102}
103
104/// Trait for fetching configuration values.
105pub trait GetConfig: Send + Sync {
106	/// Get the configuration value at the current snapshot.
107	fn get_config(&self, key: ConfigKey) -> Value;
108	/// Get the configuration value at a specific snapshot version.
109	fn get_config_at(&self, key: ConfigKey, version: CommitVersion) -> Value;
110
111	/// Get the current value as a u64. Panics if the value is not Value::Uint8.
112	fn get_config_uint8(&self, key: ConfigKey) -> u64 {
113		let val = self.get_config(key);
114		match val {
115			Value::Uint8(v) => v,
116			v => panic!("config key '{}' expected Uint8, got {:?}", key, v),
117		}
118	}
119
120	/// Get the current value as a std::time::Duration. Panics if the value is not Value::Duration.
121	fn get_config_duration(&self, key: ConfigKey) -> StdDuration {
122		let val = self.get_config(key);
123		match val {
124			Value::Duration(v) => {
125				let total_nanos =
126					(v.get_days() as i128 * 24 * 3600 * 1_000_000_000) + (v.get_nanos() as i128);
127				StdDuration::from_nanos(total_nanos.max(0) as u64)
128			}
129			v => panic!("config key '{}' expected Duration, got {:?}", key, v),
130		}
131	}
132}