use std::{sync::Arc, time::Instant};
use std::collections::HashMap;
use sqry_core::graph::CodeGraph;
use crate::config::{INTERNER_BUILDER_OVERHEAD_RATIO, WORKING_SET_MULTIPLIER};
use super::state::OldGraphToken;
#[derive(Debug)]
pub struct RetainedEntry {
pub bytes: u64,
pub graph: Arc<CodeGraph>,
pub published_at: Instant,
pub warned_past_timeout: bool,
}
#[derive(Debug, Default)]
pub struct AdmissionState {
pub loaded_bytes: u64,
pub reserved_bytes: u64,
pub retained_old: HashMap<OldGraphToken, RetainedEntry>,
}
impl AdmissionState {
#[must_use]
pub fn retained_total_bytes(&self) -> u64 {
self.retained_old.values().map(|e| e.bytes).sum()
}
#[must_use]
pub fn total_committed_bytes(&self) -> u64 {
self.loaded_bytes
.saturating_add(self.reserved_bytes)
.saturating_add(self.retained_total_bytes())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct WorkingSetInputs {
pub new_graph_final_estimate: u64,
pub staging_overhead: u64,
pub interner_snapshot_bytes: u64,
}
#[must_use]
pub fn working_set_estimate(inputs: WorkingSetInputs) -> u64 {
let WorkingSetInputs {
new_graph_final_estimate,
staging_overhead,
interner_snapshot_bytes,
} = inputs;
let grow = |bytes: u64, factor: f64| -> u64 {
let lossy_f = bytes as f64 * factor;
if !lossy_f.is_finite() || lossy_f <= 0.0 {
0
} else {
lossy_f.ceil() as u64
}
};
grow(new_graph_final_estimate, WORKING_SET_MULTIPLIER)
.saturating_add(staging_overhead)
.saturating_add(grow(
interner_snapshot_bytes,
INTERNER_BUILDER_OVERHEAD_RATIO,
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn admission_state_is_empty_by_default() {
let state = AdmissionState::default();
assert_eq!(state.loaded_bytes, 0);
assert_eq!(state.reserved_bytes, 0);
assert_eq!(state.retained_total_bytes(), 0);
assert_eq!(state.total_committed_bytes(), 0);
assert!(state.retained_old.is_empty());
}
#[test]
fn total_committed_sums_all_three_tiers() {
let state = AdmissionState {
loaded_bytes: 100_000_000,
reserved_bytes: 50_000_000,
..AdmissionState::default()
};
assert_eq!(state.total_committed_bytes(), 150_000_000);
}
#[test]
fn working_set_estimate_matches_spec_example() {
let inputs = WorkingSetInputs {
new_graph_final_estimate: 1_000_000,
staging_overhead: 50_000,
interner_snapshot_bytes: 200_000,
};
assert_eq!(working_set_estimate(inputs), 1_600_000);
}
#[test]
fn working_set_estimate_zero_inputs_is_zero() {
assert_eq!(working_set_estimate(WorkingSetInputs::default()), 0);
}
#[test]
fn working_set_estimate_ceils_fractional_contributions() {
let inputs = WorkingSetInputs {
new_graph_final_estimate: 0,
staging_overhead: 0,
interner_snapshot_bytes: 3,
};
assert_eq!(working_set_estimate(inputs), 1);
}
#[test]
fn working_set_estimate_saturates_on_absurd_inputs() {
let inputs = WorkingSetInputs {
new_graph_final_estimate: u64::MAX,
staging_overhead: u64::MAX,
interner_snapshot_bytes: u64::MAX,
};
let estimate = working_set_estimate(inputs);
assert_eq!(estimate, u64::MAX);
}
}