use std::sync::Arc;
use crate::control::state::SharedState;
pub fn save_applied_index(state: &Arc<SharedState>, group_id: u64, applied_index: u64) {
let Some(sink) = state.raft_applied_index_sink.get() else {
return;
};
if let Err(e) = sink(group_id, applied_index) {
tracing::warn!(
group_id,
applied_index,
error = %e,
"failed to persist durable raft applied index"
);
}
}
#[derive(Debug, Default)]
pub struct AppliedPrefix {
floor: Option<u64>,
broken: bool,
}
impl AppliedPrefix {
pub fn new() -> Self {
Self::default()
}
pub fn record(&mut self, index: u64, applied_ok: bool) {
if !applied_ok {
self.broken = true;
return;
}
if !self.broken {
self.floor = Some(index);
}
}
pub fn skip(&self) {}
pub fn floor(&self) -> Option<u64> {
self.floor
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_success_batch_floors_at_last_index() {
let mut prefix = AppliedPrefix::new();
for index in 7..=10 {
prefix.record(index, true);
}
assert_eq!(prefix.floor(), Some(10));
}
#[test]
fn middle_failure_floors_before_the_gap_and_never_past_it() {
let mut prefix = AppliedPrefix::new();
prefix.record(1, true);
prefix.record(2, true);
prefix.record(3, false);
prefix.record(4, true);
prefix.record(5, true);
assert_eq!(prefix.floor(), Some(2));
}
#[test]
fn all_failure_batch_saves_nothing() {
let mut prefix = AppliedPrefix::new();
prefix.record(1, false);
prefix.record(2, false);
assert_eq!(prefix.floor(), None);
}
#[test]
fn leading_failure_floors_at_nothing_despite_later_success() {
let mut prefix = AppliedPrefix::new();
prefix.record(1, false);
prefix.record(2, true);
assert_eq!(prefix.floor(), None);
}
#[test]
fn empty_batch_saves_nothing() {
assert_eq!(AppliedPrefix::new().floor(), None);
}
#[test]
fn skipped_entries_neither_advance_nor_break_the_prefix() {
let mut prefix = AppliedPrefix::new();
prefix.record(1, true);
prefix.skip();
prefix.record(3, true);
assert_eq!(prefix.floor(), Some(3));
let mut broken = AppliedPrefix::new();
broken.record(1, true);
broken.record(2, false);
broken.skip();
broken.record(4, true);
assert_eq!(broken.floor(), Some(1));
}
}