Skip to main content

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}