#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct RepackInventory {
pub loose_objects: u64,
pub loose_bytes: u64,
pub pack_count: u64,
pub pack_bytes: u64,
pub duplicate_objects: u64,
pub packed_objects: u64,
}
impl RepackInventory {
pub fn fragmentation_bps(self) -> u16 {
if self.packed_objects == 0 {
return 0;
}
let bps = self
.duplicate_objects
.saturating_mul(10_000)
.checked_div(self.packed_objects)
.unwrap_or(0);
bps.min(10_000) as u16
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RepackReason {
Manual,
LooseObjects { count: u64 },
PackCount { count: u64 },
PackBytes { bytes: u64 },
Fragmentation { basis_points: u16 },
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RepackPolicy {
pub loose_object_threshold: Option<u64>,
pub pack_count_threshold: Option<u64>,
pub pack_bytes_threshold: Option<u64>,
pub fragmentation_threshold_bps: Option<u16>,
}
impl Default for RepackPolicy {
fn default() -> Self {
Self {
loose_object_threshold: Some(10_000),
pack_count_threshold: Some(8),
pack_bytes_threshold: Some(1024 * 1024 * 1024),
fragmentation_threshold_bps: Some(1_500),
}
}
}
impl RepackPolicy {
pub fn evaluate(self, inventory: RepackInventory) -> Option<RepackReason> {
if self
.loose_object_threshold
.is_some_and(|threshold| inventory.loose_objects >= threshold)
{
return Some(RepackReason::LooseObjects {
count: inventory.loose_objects,
});
}
if self
.pack_count_threshold
.is_some_and(|threshold| inventory.pack_count >= threshold)
{
return Some(RepackReason::PackCount {
count: inventory.pack_count,
});
}
if inventory.pack_count > 1
&& self
.pack_bytes_threshold
.is_some_and(|threshold| inventory.pack_bytes >= threshold)
{
return Some(RepackReason::PackBytes {
bytes: inventory.pack_bytes,
});
}
let fragmentation = inventory.fragmentation_bps();
if self
.fragmentation_threshold_bps
.is_some_and(|threshold| fragmentation >= threshold)
{
return Some(RepackReason::Fragmentation {
basis_points: fragmentation,
});
}
None
}
}