use std::collections::VecDeque;
use super::ColdGcEntry;
use super::ColdGcTarget;
#[derive(Debug, Clone, Default)]
pub(super) struct ColdGcQueue {
pending: VecDeque<ColdGcEntry>,
next_seq: u64,
}
impl ColdGcQueue {
pub(super) fn from_parts(pending: Vec<ColdGcEntry>, next_seq: u64) -> Self {
Self {
pending: pending.into_iter().collect(),
next_seq,
}
}
pub(super) fn enqueue(&mut self, bucket_id: String, target: ColdGcTarget) {
self.enqueue_after(bucket_id, target, 0);
}
pub(super) fn enqueue_after(
&mut self,
bucket_id: String,
target: ColdGcTarget,
not_before_ms: u64,
) {
let seq = self.next_seq;
self.next_seq = self.next_seq.saturating_add(1);
self.pending.push_back(ColdGcEntry {
seq,
bucket_id,
not_before_ms,
target,
});
}
pub(super) fn ack(&mut self, up_to_seq: u64) -> u64 {
let before = self.pending.len();
while self
.pending
.front()
.is_some_and(|entry| entry.seq <= up_to_seq)
{
self.pending.pop_front();
}
u64::try_from(before - self.pending.len()).expect("removed fits u64")
}
pub(super) fn batch(&self, max: usize) -> Vec<ColdGcEntry> {
self.pending.iter().take(max).cloned().collect()
}
pub(super) fn len(&self) -> usize {
self.pending.len()
}
pub(super) fn len_for_bucket(&self, bucket_id: &str) -> usize {
self.pending
.iter()
.filter(|entry| entry.bucket_id.is_empty() || entry.bucket_id == bucket_id)
.count()
}
pub(super) fn entries(&self) -> impl Iterator<Item = &ColdGcEntry> {
self.pending.iter()
}
pub(super) fn next_seq(&self) -> u64 {
self.next_seq
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn legacy_unattributed_gc_debt_blocks_every_bucket_proof() {
let queue = ColdGcQueue::from_parts(
vec![ColdGcEntry {
seq: 7,
bucket_id: String::new(),
not_before_ms: 0,
target: ColdGcTarget::Paths(vec!["_packs/legacy.bin".to_owned()]),
}],
8,
);
assert_eq!(queue.len_for_bucket("bucket-a"), 1);
assert_eq!(queue.len_for_bucket("bucket-b"), 1);
}
}