nodedb_cluster/multi_raft/membership.rs
1//! Group-level membership helpers consumed by the tick loop's join /
2//! promotion phases.
3//!
4//! - `commit_index_for(group)`: used by the join flow to wait until a
5//! proposed `AddLearner` conf-change commits before replying to the
6//! joining node.
7//! - `ready_learners(group)`: used by the tick loop's "promote
8//! caught-up learners" phase — returns every learner in the group
9//! whose `match_index` on this (leader) node is at least the current
10//! `commit_index`, i.e. learners that have replicated enough log to be
11//! safely promoted.
12//! - `group_leader(group)`: leader id observed by this node's local
13//! RaftNode state, used by the join flow to decide redirect vs admit.
14//! - `group_role_is_leader(group)`: cheap leader-check helper.
15
16use nodedb_raft::NodeRole;
17
18use super::core::MultiRaft;
19
20impl MultiRaft {
21 /// Current commit index for a group, or `None` if the group is not
22 /// hosted on this node.
23 pub fn commit_index_for(&self, group_id: u64) -> Option<u64> {
24 self.groups.get(&group_id).map(|n| n.commit_index())
25 }
26
27 /// Learners in `group_id` whose `match_index` on this leader has
28 /// caught up to the current `commit_index` — safe to promote.
29 ///
30 /// Returns an empty vec if this node is not the leader of the group
31 /// or the group is not hosted here.
32 pub fn ready_learners(&self, group_id: u64) -> Vec<u64> {
33 let Some(node) = self.groups.get(&group_id) else {
34 return Vec::new();
35 };
36 if node.role() != NodeRole::Leader {
37 return Vec::new();
38 }
39 let commit = node.commit_index();
40 node.learners()
41 .iter()
42 .copied()
43 .filter(|&learner| node.match_index_for(learner).unwrap_or(0) >= commit)
44 .collect()
45 }
46
47 /// Observed leader id for a group (0 = unknown / no election yet).
48 pub fn group_leader(&self, group_id: u64) -> u64 {
49 self.groups
50 .get(&group_id)
51 .map(|n| n.leader_id())
52 .unwrap_or(0)
53 }
54
55 /// Whether this node is currently the leader of `group_id`.
56 pub fn group_role_is_leader(&self, group_id: u64) -> bool {
57 self.groups
58 .get(&group_id)
59 .map(|n| n.role() == NodeRole::Leader)
60 .unwrap_or(false)
61 }
62}