Skip to main content

reddb_file/
physical_metadata_policy.rs

1//! Process-wide policy knobs for physical metadata sidecars.
2//!
3//! Runtime crates decide the active storage tier; this module owns how that
4//! tier maps to file-artifact emission policy.
5
6use std::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
7
8/// Retention applied when the seq-N catalog journal is enabled at the `Max`
9/// tier. See [`seqn_journal_retention`].
10pub const DEFAULT_METADATA_JOURNAL_RETENTION: usize = 32;
11/// Retention applied when the seq-N catalog journal is opt-in enabled outside
12/// of the `Max` tier, keeping forensics surface minimal on lower tiers.
13pub const OPT_IN_METADATA_JOURNAL_RETENTION: usize = 4;
14
15// JSON sidecar policy. 0 = unset (consult env, default off), 1 = enabled,
16// 2 = disabled. Threaded as a process-global because metadata saves are reached
17// from many call sites that do not currently carry a layout handle.
18static META_JSON_SIDECAR_POLICY: AtomicU8 = AtomicU8::new(0);
19
20/// Process-wide opt-in for the legacy `<data>.meta.json` sidecar.
21pub fn set_meta_json_sidecar_enabled(enabled: bool) {
22    META_JSON_SIDECAR_POLICY.store(if enabled { 1 } else { 2 }, Ordering::Relaxed);
23}
24
25/// Whether new metadata writes should additionally emit the JSON sidecar.
26/// Defaults to `false`; opt-in via [`set_meta_json_sidecar_enabled`] or the
27/// `REDDB_META_JSON_SIDECAR=1` env var. Reads always tolerate either JSON or
28/// binary.
29pub fn meta_json_sidecar_enabled() -> bool {
30    match META_JSON_SIDECAR_POLICY.load(Ordering::Relaxed) {
31        1 => true,
32        2 => false,
33        _ => env_flag("REDDB_META_JSON_SIDECAR"),
34    }
35}
36
37// Seq-N catalog journal policy. 0 = unset (consult env, default off), 1 =
38// enabled, 2 = disabled. Mirrors the meta-json sidecar toggle but governs the
39// `<data>.meta.rdbx.seq-{N}` forensic trail emitted on every metadata save.
40static SEQN_JOURNAL_POLICY: AtomicU8 = AtomicU8::new(0);
41// Retention override. 0 = unset (consult env, default off-tier retention).
42static SEQN_JOURNAL_RETENTION: AtomicUsize = AtomicUsize::new(0);
43
44/// Process-wide opt-in for the seq-N catalog journal.
45pub fn set_seqn_journal_enabled(enabled: bool) {
46    SEQN_JOURNAL_POLICY.store(if enabled { 1 } else { 2 }, Ordering::Relaxed);
47}
48
49/// Whether new metadata saves should also emit a seq-N journal entry.
50pub fn seqn_journal_enabled() -> bool {
51    match SEQN_JOURNAL_POLICY.load(Ordering::Relaxed) {
52        1 => true,
53        2 => false,
54        _ => env_flag("REDDB_SEQN_JOURNAL"),
55    }
56}
57
58// The `fold_pager_meta` toggle (#477) is gone: ADR 0038 ยง4 phase 1 folded the
59// pager manifest into the `.rdb` unconditionally and retired the `-meta`
60// sidecar, so there is no longer a policy to express.
61
62// Fold-DWB-into-WAL policy (#478). 0 = unset (consult env, default off: keep
63// `-dwb` sidecar), 1 = enabled (emit FullPageImage WAL records before first
64// page modification per checkpoint cycle; no `-dwb` sidecar), 2 = disabled.
65static FOLD_DWB_INTO_WAL_POLICY: AtomicU8 = AtomicU8::new(0);
66
67/// Process-wide opt-in for folding the double-write buffer into the WAL via
68/// full-page-image records.
69pub fn set_fold_dwb_into_wal_enabled(enabled: bool) {
70    FOLD_DWB_INTO_WAL_POLICY.store(if enabled { 1 } else { 2 }, Ordering::Relaxed);
71}
72
73/// Whether the pager should fold DWB into WAL (no `<data>-dwb` sidecar).
74/// Reads still tolerate the legacy sidecar so existing databases keep working
75/// through the flag flip.
76pub fn fold_dwb_into_wal_enabled() -> bool {
77    match FOLD_DWB_INTO_WAL_POLICY.load(Ordering::Relaxed) {
78        1 => true,
79        2 => false,
80        _ => env_flag("REDDB_FOLD_DWB_INTO_WAL"),
81    }
82}
83
84/// Process-wide retention for the seq-N journal. `0` resets to defaults (env
85/// or off-tier baseline).
86pub fn set_seqn_journal_retention(retention: usize) {
87    SEQN_JOURNAL_RETENTION.store(retention, Ordering::Relaxed);
88}
89
90/// Resolved retention bound for the seq-N journal. Falls back to env
91/// `REDDB_SEQN_JOURNAL_RETENTION`, then to
92/// [`OPT_IN_METADATA_JOURNAL_RETENTION`].
93pub fn seqn_journal_retention() -> usize {
94    let stored = SEQN_JOURNAL_RETENTION.load(Ordering::Relaxed);
95    if stored > 0 {
96        return stored;
97    }
98    std::env::var("REDDB_SEQN_JOURNAL_RETENTION")
99        .ok()
100        .and_then(|v| v.parse::<usize>().ok())
101        .filter(|v| *v > 0)
102        .unwrap_or(OPT_IN_METADATA_JOURNAL_RETENTION)
103}
104
105fn env_flag(name: &str) -> bool {
106    std::env::var(name)
107        .ok()
108        .map(|v| matches!(v.as_str(), "1" | "true" | "TRUE" | "yes" | "on"))
109        .unwrap_or(false)
110}
111
112#[cfg(test)]
113fn reset_physical_metadata_policy_for_test() {
114    META_JSON_SIDECAR_POLICY.store(0, Ordering::Relaxed);
115    SEQN_JOURNAL_POLICY.store(0, Ordering::Relaxed);
116    FOLD_DWB_INTO_WAL_POLICY.store(0, Ordering::Relaxed);
117    SEQN_JOURNAL_RETENTION.store(0, Ordering::Relaxed);
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use std::sync::Mutex;
124
125    static POLICY_TEST_LOCK: Mutex<()> = Mutex::new(());
126
127    fn set_env(name: &str, value: &str) {
128        unsafe {
129            std::env::set_var(name, value);
130        }
131    }
132
133    fn remove_env(name: &str) {
134        unsafe {
135            std::env::remove_var(name);
136        }
137    }
138
139    #[test]
140    fn env_flags_and_explicit_overrides_drive_sidecar_policies() {
141        let _guard = POLICY_TEST_LOCK.lock().unwrap();
142        reset_physical_metadata_policy_for_test();
143
144        for value in ["1", "true", "TRUE", "yes", "on"] {
145            set_env("REDDB_META_JSON_SIDECAR", value);
146            assert!(meta_json_sidecar_enabled(), "{value}");
147        }
148        set_env("REDDB_META_JSON_SIDECAR", "false");
149        assert!(!meta_json_sidecar_enabled());
150
151        set_meta_json_sidecar_enabled(true);
152        set_env("REDDB_META_JSON_SIDECAR", "false");
153        assert!(meta_json_sidecar_enabled());
154        set_meta_json_sidecar_enabled(false);
155        set_env("REDDB_META_JSON_SIDECAR", "1");
156        assert!(!meta_json_sidecar_enabled());
157        remove_env("REDDB_META_JSON_SIDECAR");
158    }
159
160    #[test]
161    fn journal_and_fold_policy_overrides_are_independent() {
162        let _guard = POLICY_TEST_LOCK.lock().unwrap();
163        reset_physical_metadata_policy_for_test();
164
165        set_env("REDDB_SEQN_JOURNAL", "1");
166        assert!(seqn_journal_enabled());
167        set_seqn_journal_enabled(false);
168        assert!(!seqn_journal_enabled());
169        set_seqn_journal_enabled(true);
170        assert!(seqn_journal_enabled());
171        remove_env("REDDB_SEQN_JOURNAL");
172
173        set_env("REDDB_FOLD_DWB_INTO_WAL", "on");
174        assert!(fold_dwb_into_wal_enabled());
175        set_fold_dwb_into_wal_enabled(false);
176        assert!(!fold_dwb_into_wal_enabled());
177        set_fold_dwb_into_wal_enabled(true);
178        assert!(fold_dwb_into_wal_enabled());
179        remove_env("REDDB_FOLD_DWB_INTO_WAL");
180    }
181
182    #[test]
183    fn seqn_journal_retention_prefers_override_then_env_then_default() {
184        let _guard = POLICY_TEST_LOCK.lock().unwrap();
185        reset_physical_metadata_policy_for_test();
186        remove_env("REDDB_SEQN_JOURNAL_RETENTION");
187
188        assert_eq!(seqn_journal_retention(), OPT_IN_METADATA_JOURNAL_RETENTION);
189
190        set_env("REDDB_SEQN_JOURNAL_RETENTION", "12");
191        assert_eq!(seqn_journal_retention(), 12);
192        set_env("REDDB_SEQN_JOURNAL_RETENTION", "0");
193        assert_eq!(seqn_journal_retention(), OPT_IN_METADATA_JOURNAL_RETENTION);
194        set_env("REDDB_SEQN_JOURNAL_RETENTION", "bad");
195        assert_eq!(seqn_journal_retention(), OPT_IN_METADATA_JOURNAL_RETENTION);
196
197        set_seqn_journal_retention(99);
198        assert_eq!(seqn_journal_retention(), 99);
199        set_seqn_journal_retention(0);
200        assert_eq!(seqn_journal_retention(), OPT_IN_METADATA_JOURNAL_RETENTION);
201
202        remove_env("REDDB_SEQN_JOURNAL_RETENTION");
203    }
204}