use std::collections::{HashSet, VecDeque};
use crate::batch::PrefetchPlan;
use engramdb_core::layout::Layout;
pub struct StreamingPlanner {
window_badges: usize,
queue: VecDeque<(u64, u64)>,
in_window: HashSet<(u64, u64)>,
}
impl StreamingPlanner {
pub fn new(window_badges: usize) -> Self {
Self {
window_badges,
queue: VecDeque::new(),
in_window: HashSet::new(),
}
}
pub fn advance(&mut self, rows: &[u64], layout: &Layout, plan: &mut PrefetchPlan) -> usize {
let mut n_new = 0;
for &r in rows {
let (shard, badge, _) = layout.locate(r);
let key = (shard, badge);
if !self.in_window.contains(&key) {
self.queue.push_back(key);
self.in_window.insert(key);
plan.entry(shard, badge);
n_new += 1;
}
}
while self.in_window.len() > self.window_badges {
if let Some(old) = self.queue.pop_front() {
self.in_window.remove(&old);
}
}
n_new
}
pub fn window_len(&self) -> usize {
self.in_window.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn lay() -> Layout {
Layout::new(2, 10_000, 160, 1)
}
#[test]
fn first_token_all_new() {
let l = lay();
let mut sp = StreamingPlanner::new(100);
let mut plan = PrefetchPlan::default();
let rows: Vec<u64> = (0..16).map(|i| i * 1000).collect();
let n = sp.advance(&rows, &l, &mut plan);
assert_eq!(n, 16);
assert_eq!(plan.n_badges, 16);
assert_eq!(sp.window_len(), 16);
}
#[test]
fn repeated_token_reuses_window() {
let l = lay();
let mut sp = StreamingPlanner::new(100);
let mut plan = PrefetchPlan::default();
let rows: Vec<u64> = (0..16).map(|i| i * 1000).collect();
let _ = sp.advance(&rows, &l, &mut plan);
let mut plan2 = PrefetchPlan::default();
let n = sp.advance(&rows, &l, &mut plan2);
assert_eq!(n, 0, "same token again -> all badges resident");
assert_eq!(plan2.n_badges, 0);
}
#[test]
fn window_eviction_republish() {
let l = lay();
let mut sp = StreamingPlanner::new(4);
for t in 0..6 {
let mut plan = PrefetchPlan::default();
let rows: Vec<u64> = vec![t * 1000, (t + 30) * 1000];
let n = sp.advance(&rows, &l, &mut plan);
assert_eq!(n, 2, "t={t} window len={}", sp.window_len());
}
assert!(sp.window_len() <= 4);
}
}