archiver_core/flags.rs
1//! Process-global named flags (Java's `namedFlagsSet/Get/All` BPL).
2//!
3//! Operators flip these at runtime via the management API to gate
4//! features like "skip LTS reads while NFS is wedged" without restarting
5//! the appliance. Currently consumed by:
6//! - `storage::tiered::TieredStorage` reads — `SKIP_<TIER>_FOR_RETRIEVAL`
7//! - `etl::executor::EtlExecutor` writes — `SKIP_<DEST>_FOR_ETL`
8//!
9//! Java wires these through `ConfigService.getNamedFlag(...)`. We use a
10//! global so both the HTTP handler (in archiver-api) and the storage /
11//! ETL paths (in archiver-core) can share state without an extra trait.
12
13use std::sync::OnceLock;
14
15use dashmap::DashMap;
16
17fn store() -> &'static DashMap<String, bool> {
18 static STORE: OnceLock<DashMap<String, bool>> = OnceLock::new();
19 STORE.get_or_init(DashMap::new)
20}
21
22/// Set a named flag. Returns the previous value, or `false` if unset.
23pub fn set(name: &str, value: bool) -> bool {
24 store().insert(name.to_string(), value).unwrap_or(false)
25}
26
27/// Get a named flag. Returns `false` if the flag is unset.
28pub fn get(name: &str) -> bool {
29 store().get(name).map(|e| *e.value()).unwrap_or(false)
30}
31
32/// Snapshot of all flags as `(name, value)` pairs, sorted by name.
33pub fn all() -> Vec<(String, bool)> {
34 let mut pairs: Vec<(String, bool)> = store()
35 .iter()
36 .map(|e| (e.key().clone(), *e.value()))
37 .collect();
38 pairs.sort_by(|a, b| a.0.cmp(&b.0));
39 pairs
40}
41
42/// Convenience: is `SKIP_<tier>_FOR_RETRIEVAL` set? `tier` is uppercased.
43pub fn skip_tier_for_retrieval(tier: &str) -> bool {
44 get(&format!("SKIP_{}_FOR_RETRIEVAL", tier.to_uppercase()))
45}
46
47/// Convenience: is `SKIP_<tier>_FOR_ETL` set?
48pub fn skip_tier_for_etl(tier: &str) -> bool {
49 get(&format!("SKIP_{}_FOR_ETL", tier.to_uppercase()))
50}