rocketmq_protocol/protocol/body/
controller_rollout.rs1use std::collections::{BTreeMap, BTreeSet};
16
17use serde::{Deserialize, Serialize};
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
22#[serde(rename_all = "camelCase")]
23pub struct RolloutQuorumStatus {
24 pub schema_version: u32,
25 pub target_node_id: u64,
26 pub leader_id: u64,
27 pub voters: Vec<u64>,
28 pub peer_endpoints: BTreeMap<u64, String>,
30 pub remaining_ready_voters: Vec<u64>,
31 pub allowed: bool,
32}
33
34impl RolloutQuorumStatus {
35 pub fn permits(&self, target: u64) -> bool {
37 let voters = self.voters.iter().copied().collect::<BTreeSet<_>>();
38 let remaining = self.remaining_ready_voters.iter().copied().collect::<BTreeSet<_>>();
39 self.schema_version == 1
40 && self.allowed
41 && target != 0
42 && self.target_node_id == target
43 && voters.len() >= 3
44 && voters.len() == self.voters.len()
45 && !voters.contains(&0)
46 && voters.contains(&target)
47 && voters.contains(&self.leader_id)
48 && self.peer_endpoints.keys().copied().collect::<BTreeSet<_>>() == voters
49 && self.peer_endpoints.values().all(|endpoint| !endpoint.is_empty())
50 && remaining.len() == self.remaining_ready_voters.len()
51 && !remaining.contains(&target)
52 && remaining.is_subset(&voters)
53 && remaining.len() > voters.len() / 2
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn rollout_observation_rejects_unsupported_malformed_or_insufficient_majorities() {
63 let good = RolloutQuorumStatus {
64 schema_version: 1,
65 target_node_id: 2,
66 leader_id: 1,
67 voters: vec![1, 2, 3],
68 peer_endpoints: (1..=3).map(|id| (id, format!("node-{id}:9879"))).collect(),
69 remaining_ready_voters: vec![1, 3],
70 allowed: true,
71 };
72 assert!(good.permits(2));
73 assert!(!good.permits(1));
74 let mut bad = good.clone();
75 bad.schema_version = 2;
76 assert!(!bad.permits(2));
77 bad = good.clone();
78 bad.remaining_ready_voters = vec![1, 1];
79 assert!(!bad.permits(2));
80 bad = good.clone();
81 bad.remaining_ready_voters = vec![1, 4];
82 assert!(!bad.permits(2));
83 bad = good.clone();
84 bad.remaining_ready_voters = vec![1, 2];
85 assert!(!bad.permits(2));
86 bad = good.clone();
87 bad.allowed = false;
88 assert!(!bad.permits(2));
89 bad = good.clone();
90 bad.voters.push(3);
91 assert!(!bad.permits(2));
92 assert!(serde_json::from_str::<RolloutQuorumStatus>("{}").is_err());
93 assert_eq!(
94 serde_json::from_slice::<RolloutQuorumStatus>(&serde_json::to_vec(&good).unwrap()).unwrap(),
95 good
96 );
97 }
98}