#[derive(Clone, Copy, Debug)]
pub struct SnapshotPolicy {
every_n_decided: u64,
last_snapshot_at: u64,
}
impl SnapshotPolicy {
#[must_use]
pub const fn every(every_n_decided: u64) -> Self {
Self {
every_n_decided,
last_snapshot_at: 0,
}
}
#[must_use]
pub const fn disabled() -> Self {
Self::every(0)
}
pub fn rebase(&mut self, decided_idx: u64) {
self.last_snapshot_at = decided_idx;
}
pub fn should_snapshot(&mut self, decided_idx: u64) -> bool {
if self.every_n_decided == 0 {
return false;
}
if decided_idx >= self.last_snapshot_at + self.every_n_decided {
self.last_snapshot_at = decided_idx;
true
} else {
false
}
}
}
impl Default for SnapshotPolicy {
fn default() -> Self {
Self::disabled()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn disabled_never_triggers() {
let mut policy = SnapshotPolicy::disabled();
assert!(!policy.should_snapshot(1));
assert!(!policy.should_snapshot(1_000_000));
}
#[test]
fn every_n_fires_at_multiples() {
let mut policy = SnapshotPolicy::every(100);
assert!(!policy.should_snapshot(50));
assert!(policy.should_snapshot(100));
assert!(!policy.should_snapshot(150));
assert!(policy.should_snapshot(200));
}
#[test]
fn every_n_advances_remembers_last_trigger() {
let mut policy = SnapshotPolicy::every(10);
assert!(policy.should_snapshot(10));
assert!(!policy.should_snapshot(10));
assert!(!policy.should_snapshot(15));
assert!(policy.should_snapshot(20));
}
#[test]
fn default_is_disabled() {
let mut policy = SnapshotPolicy::default();
assert!(!policy.should_snapshot(u64::MAX));
}
#[test]
fn rebase_suppresses_spurious_first_trigger_after_restart() {
let mut policy = SnapshotPolicy::every(100);
policy.rebase(5_000);
assert!(
!policy.should_snapshot(5_000),
"must not snapshot on the first decision after recovery",
);
assert!(!policy.should_snapshot(5_099));
assert!(
policy.should_snapshot(5_100),
"the every-N cadence resumes relative to the rebased baseline",
);
}
#[test]
fn rebase_on_disabled_policy_stays_disabled() {
let mut policy = SnapshotPolicy::disabled();
policy.rebase(5_000);
assert!(!policy.should_snapshot(u64::MAX));
}
}