use tracing::debug;
use crate::forward::PlanExecutor;
use super::loop_core::{CommitApplier, RaftLoop};
use super::membership_convergence::plan_leaving_voters;
impl<A: CommitApplier, P: PlanExecutor> RaftLoop<A, P> {
pub(super) fn transfer_leadership_for_leaving_voters(&self) {
let rf = self.replication_factor() as usize;
let transfers: Vec<(u64, u64)> = {
let mr = self.multi_raft.lock().unwrap_or_else(|p| p.into_inner());
let group_ids = mr.group_ids();
let mut out = Vec::new();
for gid in group_ids {
if gid == crate::metadata_group::METADATA_GROUP_ID
|| gid == crate::calvin::sequencer::SEQUENCER_GROUP_ID
{
continue;
}
if !mr.group_role_is_leader(gid) {
continue;
}
let placement: Option<Vec<u64>> = mr
.routing()
.read()
.unwrap_or_else(|p| p.into_inner())
.group_info(gid)
.and_then(|info| info.placement.clone());
let Some(placement) = placement else {
continue;
};
let Some(m) = mr.group_membership(gid) else {
continue;
};
let self_is_leaving =
plan_leaving_voters(&m.voters, &placement, rf).contains(&m.leader_id);
if !self_is_leaving {
continue;
}
let target = placement
.iter()
.copied()
.find(|t| *t != m.leader_id && m.voters.contains(t));
if let Some(target) = target {
out.push((gid, target));
} else {
debug!(
group_id = gid,
"step-aside: leader is a leaving voter but no in-placement \
voter target yet; deferring transfer"
);
}
}
out
};
for (group_id, target) in transfers {
let mut mr = self.multi_raft.lock().unwrap_or_else(|p| p.into_inner());
match mr.transfer_leadership(group_id, target) {
Ok(()) => {
debug!(
group_id,
target, "step-aside: initiated leadership transfer"
);
}
Err(e) => {
debug!(
group_id,
target,
error = %e,
"step-aside: leadership transfer deferred"
);
}
}
}
}
}