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