use std::collections::HashSet;
use crate::infinitedb_core::{
address::RevisionId,
block::{BlockId, Record},
snapshot::Snapshot,
};
#[derive(Debug, Clone)]
pub struct RetentionPolicy {
pub tombstone_horizon: RevisionId,
pub version_horizon: RevisionId,
}
impl RetentionPolicy {
pub fn keep_all() -> Self {
Self {
tombstone_horizon: RevisionId::ZERO,
version_horizon: RevisionId::ZERO,
}
}
}
pub fn apply_retention(records: Vec<Record>, policy: &RetentionPolicy) -> Vec<Record> {
records
.into_iter()
.filter(|r| {
if r.tombstone && policy.tombstone_horizon > RevisionId::ZERO {
return r.revision >= policy.tombstone_horizon;
}
true
})
.collect()
}
pub fn safe_to_delete(
superseded: &[BlockId],
live_snapshots: &[Snapshot],
) -> Vec<BlockId> {
let referenced: HashSet<BlockId> = live_snapshots
.iter()
.flat_map(|s| s.blocks.values().copied())
.collect();
superseded
.iter()
.filter(|id| !referenced.contains(id))
.copied()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::infinitedb_core::{
address::{Address, DimensionVector, RevisionId, SpaceId},
block::Record,
};
fn make_record(rev: u64, tombstone: bool) -> Record {
Record {
address: Address::new(SpaceId(1), DimensionVector::new(vec![0, 0])),
revision: RevisionId(rev),
data: vec![],
tombstone,
}
}
#[test]
fn prunes_old_tombstones() {
let records = vec![
make_record(1, true), make_record(5, true), make_record(3, false), ];
let policy = RetentionPolicy {
tombstone_horizon: RevisionId(5),
version_horizon: RevisionId::ZERO,
};
let kept = apply_retention(records, &policy);
assert_eq!(kept.len(), 2);
assert!(kept.iter().all(|r| !r.tombstone || r.revision.0 >= 5));
}
#[test]
fn keeps_all_when_horizon_is_zero() {
let records = vec![make_record(1, true), make_record(2, false)];
let policy = RetentionPolicy::keep_all();
let kept = apply_retention(records, &policy);
assert_eq!(kept.len(), 2);
}
}