reddb_server/replication/
commit_policy.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub enum CommitPolicy {
24 #[default]
25 Local,
26 RemoteWal,
27 AckN(u32),
28 Quorum,
29}
30
31impl CommitPolicy {
32 pub fn label(self) -> &'static str {
33 match self {
34 Self::Local => "local",
35 Self::RemoteWal => "remote_wal",
36 Self::AckN(_) => "ack_n",
37 Self::Quorum => "quorum",
38 }
39 }
40
41 pub fn detail_label(self) -> String {
42 match self {
43 Self::AckN(n) => format!("ack_n={n}"),
44 _ => self.label().to_string(),
45 }
46 }
47
48 pub fn parse_strict(raw: &str) -> Option<Self> {
49 let lower = raw.trim().to_ascii_lowercase();
50 if lower == "local" {
51 return Some(Self::Local);
52 }
53 if lower == "remote_wal" {
54 return Some(Self::RemoteWal);
55 }
56 if lower == "quorum" {
57 return Some(Self::Quorum);
58 }
59 if let Some(n_str) = lower.strip_prefix("ack_n=") {
60 return n_str.parse::<u32>().ok().map(Self::AckN);
61 }
62 None
63 }
64
65 pub fn from_env() -> Self {
69 match std::env::var("RED_PRIMARY_COMMIT_POLICY").ok() {
70 Some(raw) => Self::parse(raw.trim()),
71 None => Self::Local,
72 }
73 }
74
75 pub fn parse(raw: &str) -> Self {
76 let lower = raw.to_ascii_lowercase();
77 if lower == "local" || lower.is_empty() {
78 return Self::Local;
79 }
80 if lower == "remote_wal" {
81 return Self::RemoteWal;
82 }
83 if lower == "quorum" {
84 return Self::Quorum;
85 }
86 if let Some(n_str) = lower.strip_prefix("ack_n=") {
87 if let Ok(n) = n_str.parse::<u32>() {
88 return Self::AckN(n);
89 }
90 }
91 tracing::warn!(
92 target: "reddb::replication::commit_policy",
93 value = %raw,
94 "unknown RED_PRIMARY_COMMIT_POLICY; falling back to local"
95 );
96 Self::Local
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn default_is_local() {
106 assert_eq!(CommitPolicy::default(), CommitPolicy::Local);
107 }
108
109 #[test]
110 fn parse_known_values() {
111 assert_eq!(CommitPolicy::parse("local"), CommitPolicy::Local);
112 assert_eq!(CommitPolicy::parse("LOCAL"), CommitPolicy::Local);
113 assert_eq!(CommitPolicy::parse("remote_wal"), CommitPolicy::RemoteWal);
114 assert_eq!(CommitPolicy::parse("quorum"), CommitPolicy::Quorum);
115 assert_eq!(CommitPolicy::parse("ack_n=3"), CommitPolicy::AckN(3));
116 assert_eq!(CommitPolicy::parse("ack_n=0"), CommitPolicy::AckN(0));
117 }
118
119 #[test]
120 fn parse_unknown_falls_back_to_local() {
121 assert_eq!(CommitPolicy::parse("nonsense"), CommitPolicy::Local);
122 assert_eq!(CommitPolicy::parse("ack_n=abc"), CommitPolicy::Local);
123 assert_eq!(CommitPolicy::parse(""), CommitPolicy::Local);
124 }
125
126 #[test]
127 fn label_round_trips_known_values() {
128 assert_eq!(CommitPolicy::Local.label(), "local");
129 assert_eq!(CommitPolicy::RemoteWal.label(), "remote_wal");
130 assert_eq!(CommitPolicy::AckN(5).label(), "ack_n");
131 assert_eq!(CommitPolicy::Quorum.label(), "quorum");
132 }
133
134 #[test]
135 fn strict_parse_rejects_unknown_values() {
136 assert_eq!(
137 CommitPolicy::parse_strict("ack_n=2"),
138 Some(CommitPolicy::AckN(2))
139 );
140 assert_eq!(CommitPolicy::parse_strict("nonsense"), None);
141 assert_eq!(CommitPolicy::parse_strict(""), None);
142 }
143}