use nodedb_raft::NodeRole;
use crate::error::{ClusterError, Result};
use super::core::MultiRaft;
impl MultiRaft {
pub fn group_contains_node(&self, group_id: u64, node_id: u64) -> Option<bool> {
let membership = self.group_membership(group_id)?;
Some(membership.voters.contains(&node_id) || membership.learners.contains(&node_id))
}
pub fn commit_index_for(&self, group_id: u64) -> Option<u64> {
self.groups.get(&group_id).map(|n| n.commit_index())
}
pub fn ready_learners(&self, group_id: u64) -> Vec<u64> {
let Some(node) = self.groups.get(&group_id) else {
return Vec::new();
};
if node.role() != NodeRole::Leader {
return Vec::new();
}
let commit = node.commit_index();
node.learners()
.iter()
.copied()
.filter(|&learner| node.match_index_for(learner).unwrap_or(0) >= commit)
.collect()
}
pub fn group_leader(&self, group_id: u64) -> u64 {
self.groups
.get(&group_id)
.map(|n| n.leader_id())
.unwrap_or(0)
}
pub fn group_role_is_leader(&self, group_id: u64) -> bool {
self.groups
.get(&group_id)
.map(|n| n.role() == NodeRole::Leader)
.unwrap_or(false)
}
pub fn transfer_leadership(&mut self, group_id: u64, target: u64) -> Result<()> {
let node = self
.groups
.get_mut(&group_id)
.ok_or(ClusterError::GroupNotFound { group_id })?;
node.transfer_leadership(target).map_err(ClusterError::Raft)
}
}
#[cfg(test)]
mod tests {
use std::time::{Duration, Instant};
use super::*;
use crate::routing::RoutingTable;
fn make_multi_raft_single_node(node_id: u64) -> MultiRaft {
let dir = tempfile::tempdir().unwrap();
let rt = RoutingTable::uniform(1, &[node_id], 1);
let mut mr = MultiRaft::new(node_id, rt, dir.path().to_path_buf());
mr.add_group(0, vec![]).unwrap();
mr
}
#[test]
fn transfer_leadership_group_not_found() {
let mut mr = make_multi_raft_single_node(1);
let err = mr.transfer_leadership(999, 2).unwrap_err();
assert!(
matches!(err, ClusterError::GroupNotFound { group_id: 999 }),
"expected GroupNotFound, got {err:?}"
);
}
#[test]
fn transfer_leadership_delegates_to_raft_node() {
let mut mr = make_multi_raft_single_node(1);
if let Some(node) = mr.groups_mut().get_mut(&0) {
node.election_deadline_override(Instant::now() - Duration::from_millis(1));
node.tick();
}
let err = mr.transfer_leadership(0, 1).unwrap_err();
assert!(
matches!(err, ClusterError::Raft(_)),
"expected Raft error from transfer_leadership, got {err:?}"
);
}
}