use core::cmp::{max, Ordering};
use core::num::NonZeroU32;
use ::rand::RngExt;
use crate::rng;
use crate::segments::*;
use crate::Random;
use crate::*;
mod ghost;
mod policy;
pub(crate) use ghost::GhostQueue;
pub use policy::Policy;
pub struct Eviction {
policy: Policy,
last_update_time: Instant,
ranked_segs: Box<[Option<NonZeroU32>]>,
index: usize,
rng: Box<Random>,
pub(crate) ghost: GhostQueue,
}
impl Eviction {
pub fn new(nseg: usize, policy: Policy) -> Self {
let mut ranked_segs = Vec::with_capacity(0);
ranked_segs.reserve_exact(nseg);
ranked_segs.resize_with(nseg, || None);
let ranked_segs = ranked_segs.into_boxed_slice();
let ghost_capacity = if matches!(policy, Policy::S3Fifo { .. }) {
std::cmp::max(1024, nseg * 64)
} else {
0
};
Self {
policy,
last_update_time: Instant::now(),
ranked_segs,
index: 0,
rng: Box::new(rng()),
ghost: GhostQueue::new(ghost_capacity),
}
}
#[inline]
pub fn policy(&self) -> Policy {
self.policy
}
pub fn least_valuable_seg(&mut self) -> Option<NonZeroU32> {
let index = self.index;
self.index += 1;
if index < self.ranked_segs.len() {
self.ranked_segs[index]
} else {
None
}
}
#[inline]
pub fn random(&mut self) -> u32 {
self.rng.random()
}
pub fn should_rerank(&mut self) -> bool {
let now = Instant::now();
match self.policy {
Policy::None
| Policy::Random
| Policy::RandomFifo
| Policy::Merge { .. }
| Policy::S3Fifo { .. } => false,
Policy::Fifo | Policy::Cte | Policy::Util => {
if self.ranked_segs[0].is_none()
|| (now - self.last_update_time).as_secs() > 1
|| self.ranked_segs.len() < (self.index + 8)
{
self.last_update_time = now;
true
} else {
false
}
}
}
}
pub fn rerank(&mut self, headers: &[SegmentHeader]) {
let mut ids: Vec<NonZeroU32> = headers.iter().map(|h| h.id()).collect();
match self.policy {
Policy::None
| Policy::Random
| Policy::RandomFifo
| Policy::Merge { .. }
| Policy::S3Fifo { .. } => {
return;
}
Policy::Fifo => {
ids.sort_by(|a, b| {
Self::compare_fifo(
&headers[a.get() as usize - 1],
&headers[b.get() as usize - 1],
)
});
}
Policy::Cte => {
ids.sort_by(|a, b| {
Self::compare_cte(
&headers[a.get() as usize - 1],
&headers[b.get() as usize - 1],
)
});
}
Policy::Util => {
ids.sort_by(|a, b| {
Self::compare_util(
&headers[a.get() as usize - 1],
&headers[b.get() as usize - 1],
)
});
}
}
for (i, id) in self.ranked_segs.iter_mut().enumerate() {
*id = Some(ids[i]);
}
self.index = 0;
}
fn compare_fifo(lhs: &SegmentHeader, rhs: &SegmentHeader) -> Ordering {
if !lhs.can_evict() {
Ordering::Greater
} else if !rhs.can_evict() {
Ordering::Less
} else if max(lhs.create_at(), lhs.merge_at()) > max(rhs.create_at(), rhs.merge_at()) {
Ordering::Greater
} else {
Ordering::Less
}
}
fn compare_cte(lhs: &SegmentHeader, rhs: &SegmentHeader) -> Ordering {
if !lhs.can_evict() {
Ordering::Greater
} else if !rhs.can_evict() {
Ordering::Less
} else if (lhs.create_at() + lhs.ttl()) > (rhs.create_at() + rhs.ttl()) {
Ordering::Greater
} else {
Ordering::Less
}
}
fn compare_util(lhs: &SegmentHeader, rhs: &SegmentHeader) -> Ordering {
if !lhs.can_evict() {
Ordering::Greater
} else if !rhs.can_evict() {
Ordering::Less
} else if lhs.live_bytes() > rhs.live_bytes() {
Ordering::Greater
} else {
Ordering::Less
}
}
#[inline]
pub fn max_merge(&self) -> usize {
if let Policy::Merge { max, .. } = self.policy {
max
} else {
8
}
}
#[inline]
pub fn n_merge(&self) -> usize {
if let Policy::Merge { merge, .. } = self.policy {
merge
} else {
4
}
}
#[inline]
pub fn n_compact(&self) -> usize {
if let Policy::Merge { compact, .. } = self.policy {
compact
} else {
2
}
}
#[inline]
pub fn compact_ratio(&self) -> f64 {
if self.n_compact() == 0 {
0.0
} else {
1.0 / self.n_compact() as f64
}
}
#[inline]
pub fn target_ratio(&self) -> f64 {
1.0 / self.n_merge() as f64
}
#[inline]
pub fn stop_ratio(&self) -> f64 {
self.target_ratio() * (self.n_merge() - 1) as f64 + 0.05
}
}