1use hashbrown::{HashMap, HashSet};
5
6use crate::node_type::NodeType;
7use crate::quorum_policy::QuorumPolicy;
8use crate::rep_node::RepNode;
9
10#[derive(Debug, Clone)]
15pub struct RepGroup {
16 name: String,
18 group_id: u64,
20 nodes: HashMap<String, RepNode>,
22 quorum_policy: QuorumPolicy,
24}
25
26impl RepGroup {
27 pub fn new(name: String, group_id: u64) -> Self {
32 Self {
33 name,
34 group_id,
35 nodes: HashMap::new(),
36 quorum_policy: QuorumPolicy::SimpleMajority,
37 }
38 }
39
40 pub fn with_policy(
42 name: String,
43 group_id: u64,
44 policy: QuorumPolicy,
45 ) -> Self {
46 Self { name, group_id, nodes: HashMap::new(), quorum_policy: policy }
47 }
48
49 pub fn set_quorum_policy(&mut self, policy: QuorumPolicy) {
51 self.quorum_policy = policy;
52 }
53
54 pub fn quorum_policy(&self) -> &QuorumPolicy {
56 &self.quorum_policy
57 }
58
59 pub fn name(&self) -> &str {
61 &self.name
62 }
63
64 pub fn group_id(&self) -> u64 {
66 self.group_id
67 }
68
69 pub fn add_node(&mut self, node: RepNode) -> Option<RepNode> {
72 self.nodes.insert(node.name.clone(), node)
73 }
74
75 pub fn remove_node(&mut self, name: &str) -> Option<RepNode> {
78 self.nodes.remove(name)
79 }
80
81 pub fn get_node(&self, name: &str) -> Option<&RepNode> {
83 self.nodes.get(name)
84 }
85
86 pub fn get_nodes(&self) -> Vec<&RepNode> {
88 self.nodes.values().collect()
89 }
90
91 pub fn get_electable_nodes(&self) -> Vec<&RepNode> {
93 self.nodes.values().filter(|n| n.node_type().is_electable()).collect()
94 }
95
96 pub fn get_monitors(&self) -> Vec<&RepNode> {
98 self.nodes
99 .values()
100 .filter(|n| n.node_type() == NodeType::Monitor)
101 .collect()
102 }
103
104 pub fn electable_count(&self) -> u32 {
106 self.nodes.values().filter(|n| n.node_type().is_electable()).count()
107 as u32
108 }
109
110 pub fn phase1_quorum(&self) -> usize {
112 self.quorum_policy.phase1_quorum(self.electable_count() as usize)
113 }
114
115 pub fn phase2_quorum(&self) -> usize {
117 self.quorum_policy.phase2_quorum(self.electable_count() as usize)
118 }
119
120 pub fn is_valid_phase2_quorum(&self, voters: &HashSet<&str>) -> bool {
122 self.quorum_policy
123 .is_valid_phase2_quorum(voters, self.electable_count() as usize)
124 }
125
126 pub fn rebuild_quorum_system(&self) -> Result<(), String> {
132 self.quorum_policy.validate(self.electable_count() as usize)
133 }
134
135 pub fn quorum_size(&self) -> u32 {
145 self.phase2_quorum() as u32
146 }
147
148 pub fn contains_node(&self, name: &str) -> bool {
150 self.nodes.contains_key(name)
151 }
152
153 pub fn node_count(&self) -> usize {
155 self.nodes.len()
156 }
157}
158
159impl std::fmt::Display for RepGroup {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(
162 f,
163 "RepGroup(name={}, id={}, nodes={})",
164 self.name,
165 self.group_id,
166 self.nodes.len()
167 )
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn make_electable(name: &str, id: u32) -> RepNode {
176 RepNode::new(
177 name.to_string(),
178 NodeType::Electable,
179 "localhost".to_string(),
180 5000 + id as u16,
181 id,
182 )
183 }
184
185 fn make_monitor(name: &str, id: u32) -> RepNode {
186 RepNode::new(
187 name.to_string(),
188 NodeType::Monitor,
189 "localhost".to_string(),
190 5000 + id as u16,
191 id,
192 )
193 }
194
195 fn make_secondary(name: &str, id: u32) -> RepNode {
196 RepNode::new(
197 name.to_string(),
198 NodeType::Secondary,
199 "localhost".to_string(),
200 5000 + id as u16,
201 id,
202 )
203 }
204
205 fn make_arbiter(name: &str, id: u32) -> RepNode {
206 RepNode::new(
207 name.to_string(),
208 NodeType::Arbiter,
209 "localhost".to_string(),
210 5000 + id as u16,
211 id,
212 )
213 }
214
215 #[test]
216 fn test_new_group() {
217 let group = RepGroup::new("testgroup".to_string(), 1);
218 assert_eq!(group.name(), "testgroup");
219 assert_eq!(group.group_id(), 1);
220 assert_eq!(group.node_count(), 0);
221 }
222
223 #[test]
224 fn test_add_and_get_node() {
225 let mut group = RepGroup::new("g".to_string(), 1);
226 let node = make_electable("n1", 1);
227 assert!(group.add_node(node).is_none());
228 assert!(group.get_node("n1").is_some());
229 assert_eq!(group.get_node("n1").unwrap().name(), "n1");
230 }
231
232 #[test]
233 fn test_add_replaces_existing() {
234 let mut group = RepGroup::new("g".to_string(), 1);
235 group.add_node(make_electable("n1", 1));
236 let old = group.add_node(make_electable("n1", 2));
237 assert!(old.is_some());
238 assert_eq!(old.unwrap().node_id(), 1);
239 assert_eq!(group.get_node("n1").unwrap().node_id(), 2);
240 }
241
242 #[test]
243 fn test_remove_node() {
244 let mut group = RepGroup::new("g".to_string(), 1);
245 group.add_node(make_electable("n1", 1));
246 let removed = group.remove_node("n1");
247 assert!(removed.is_some());
248 assert!(!group.contains_node("n1"));
249 assert!(group.remove_node("n1").is_none());
250 }
251
252 #[test]
253 fn test_contains_node() {
254 let mut group = RepGroup::new("g".to_string(), 1);
255 assert!(!group.contains_node("n1"));
256 group.add_node(make_electable("n1", 1));
257 assert!(group.contains_node("n1"));
258 }
259
260 #[test]
261 fn test_get_nodes() {
262 let mut group = RepGroup::new("g".to_string(), 1);
263 group.add_node(make_electable("n1", 1));
264 group.add_node(make_monitor("m1", 2));
265 assert_eq!(group.get_nodes().len(), 2);
266 }
267
268 #[test]
269 fn test_get_electable_nodes() {
270 let mut group = RepGroup::new("g".to_string(), 1);
271 group.add_node(make_electable("n1", 1));
272 group.add_node(make_electable("n2", 2));
273 group.add_node(make_monitor("m1", 3));
274 group.add_node(make_secondary("s1", 4));
275 group.add_node(make_arbiter("a1", 5));
276
277 let electables = group.get_electable_nodes();
278 assert_eq!(electables.len(), 3);
280 }
281
282 #[test]
283 fn test_get_monitors() {
284 let mut group = RepGroup::new("g".to_string(), 1);
285 group.add_node(make_electable("n1", 1));
286 group.add_node(make_monitor("m1", 2));
287 group.add_node(make_monitor("m2", 3));
288
289 let monitors = group.get_monitors();
290 assert_eq!(monitors.len(), 2);
291 }
292
293 #[test]
294 fn test_electable_count() {
295 let mut group = RepGroup::new("g".to_string(), 1);
296 assert_eq!(group.electable_count(), 0);
297
298 group.add_node(make_electable("n1", 1));
299 group.add_node(make_electable("n2", 2));
300 group.add_node(make_monitor("m1", 3));
301 assert_eq!(group.electable_count(), 2);
302
303 group.add_node(make_arbiter("a1", 4));
304 assert_eq!(group.electable_count(), 3);
305 }
306
307 #[test]
308 fn test_quorum_size_empty() {
309 let group = RepGroup::new("g".to_string(), 1);
310 assert_eq!(group.quorum_size(), 0);
311 }
312
313 #[test]
314 fn test_quorum_size_one() {
315 let mut group = RepGroup::new("g".to_string(), 1);
316 group.add_node(make_electable("n1", 1));
317 assert_eq!(group.quorum_size(), 1);
319 }
320
321 #[test]
322 fn test_quorum_size_two() {
323 let mut group = RepGroup::new("g".to_string(), 1);
324 group.add_node(make_electable("n1", 1));
325 group.add_node(make_electable("n2", 2));
326 assert_eq!(group.quorum_size(), 2);
328 }
329
330 #[test]
331 fn test_quorum_size_three() {
332 let mut group = RepGroup::new("g".to_string(), 1);
333 group.add_node(make_electable("n1", 1));
334 group.add_node(make_electable("n2", 2));
335 group.add_node(make_electable("n3", 3));
336 assert_eq!(group.quorum_size(), 2);
338 }
339
340 #[test]
341 fn test_quorum_size_five() {
342 let mut group = RepGroup::new("g".to_string(), 1);
343 for i in 1..=5 {
344 group.add_node(make_electable(&format!("n{}", i), i));
345 }
346 assert_eq!(group.quorum_size(), 3);
348 }
349
350 #[test]
351 fn test_quorum_ignores_non_electable() {
352 let mut group = RepGroup::new("g".to_string(), 1);
353 group.add_node(make_electable("n1", 1));
354 group.add_node(make_electable("n2", 2));
355 group.add_node(make_electable("n3", 3));
356 group.add_node(make_monitor("m1", 4));
357 group.add_node(make_secondary("s1", 5));
358 assert_eq!(group.quorum_size(), 2);
360 }
361
362 #[test]
363 fn test_display() {
364 let mut group = RepGroup::new("mygroup".to_string(), 42);
365 group.add_node(make_electable("n1", 1));
366 let s = group.to_string();
367 assert!(s.contains("mygroup"));
368 assert!(s.contains("42"));
369 assert!(s.contains("1"));
370 }
371
372 #[test]
373 fn test_clone() {
374 let mut group = RepGroup::new("g".to_string(), 1);
375 group.add_node(make_electable("n1", 1));
376 let cloned = group.clone();
377 assert_eq!(cloned.name(), group.name());
378 assert_eq!(cloned.node_count(), group.node_count());
379 }
380
381 #[test]
382 fn test_get_node_not_found() {
383 let group = RepGroup::new("g".to_string(), 1);
384 assert!(group.get_node("nonexistent").is_none());
385 }
386}