This commit is contained in:
Mateusz Gruszczyński committed 2026-10-04 09:29:10 +02:00
1 parent 2e4aa4df4d
commit 87e571c170
106 files changed
+17952 -6185

No files matched your search

+80 -18
View File
@@ -84,6 +84,7 @@ impl Cache {
_ = maintenance_shutdown.cancelled() => break,
_ = tick.tick() => {
let Some(cache) = weak.upgrade() else { break; };
cache.trim_hot_idle();
cache.demote_idle();
cache.sweep_cold().await;
cache.gc_banned(256);
@@ -94,7 +95,9 @@ impl Cache {
let weak = Arc::downgrade(&cache);
tokio::spawn(async move {
loop {
let Some(cache) = weak.upgrade() else { break; };
let Some(cache) = weak.upgrade() else {
break;
};
let interval = cache
.tier_settings
.lock()
@@ -156,8 +159,8 @@ impl Cache {
}
let lease = self.try_reserve_transient(bytes, settings)?;
let mut store = self.store.lock().unwrap_or_else(|e| e.into_inner());
let fill_now = usize::try_from(self.fill_bytes_active.load(Ordering::Relaxed))
.unwrap_or(usize::MAX);
let fill_now =
usize::try_from(self.fill_bytes_active.load(Ordering::Relaxed)).unwrap_or(usize::MAX);
let allowed = settings
.max_bytes
.saturating_add(Self::fill_headroom_bytes(settings.max_bytes));
@@ -165,20 +168,14 @@ impl Cache {
let generation = store.generation;
let mut removed = 0usize;
let mut released_hot = false;
while store
.bytes
.saturating_add(fill_now)
.saturating_add(bytes)
> allowed
{
while store.bytes.saturating_add(fill_now).saturating_add(bytes) > allowed {
if removed >= settings.hot_eviction_max_objects {
self.counters
.hot_eviction_limited
.fetch_add(1, Ordering::Relaxed);
break;
}
let Some((_, key)) = store.lru.first_key_value().map(|(seq, key)| (*seq, *key))
else {
let Some((_, key)) = store.lru.first_key_value().map(|(seq, key)| (*seq, *key)) else {
break;
};
let Some(old) = store.remove(&key) else {
@@ -210,11 +207,7 @@ impl Cache {
self.counters.evictions.fetch_add(1, Ordering::Relaxed);
}
}
let fits = store
.bytes
.saturating_add(fill_now)
.saturating_add(bytes)
<= allowed;
let fits = store.bytes.saturating_add(fill_now).saturating_add(bytes) <= allowed;
if fits {
self.fill_bytes_active
.fetch_add(bytes as u64, Ordering::Relaxed);
@@ -234,6 +227,76 @@ impl Cache {
}
}
/// Bound long-lived HOT RSS without flushing the working set. Only entries
/// idle for the configured age are considered, and only while HOT usage is
/// above the configured target waterline.
fn trim_hot_idle(&self) {
let settings = self
.tier_settings
.lock()
.unwrap_or_else(|e| e.into_inner())
.clone();
if !settings.hot_auto_trim_enabled {
return;
}
let target = settings
.max_bytes
.saturating_mul(settings.hot_auto_trim_target_percent as usize)
/ 100;
let idle_cutoff = Duration::from_secs(settings.hot_auto_trim_after_seconds);
let mut store = self.store.lock().unwrap_or_else(|e| e.into_inner());
if store.bytes <= target {
return;
}
let revision = store.revision;
let generation = store.generation;
let mut removed = 0usize;
while store.bytes > target && removed < settings.hot_eviction_max_objects {
let Some((_, key)) = store.lru.first_key_value().map(|(seq, key)| (*seq, *key)) else {
break;
};
let idle = store
.entries
.get(&key)
.is_some_and(|entry| entry.last_access.elapsed() >= idle_cutoff);
if !idle {
// LRU order is also last-access order, so newer entries cannot qualify.
break;
}
let Some(entry) = store.remove(&key) else {
continue;
};
removed += 1;
let scope_generation = store
.scope_generations
.get(&entry.scope)
.copied()
.unwrap_or(0);
let key_generation = store
.key_generations
.get(&entry.base_key)
.copied()
.unwrap_or(0);
if !(settings.cold_enabled
&& settings.cold_on_lru
&& self.enqueue_demotion(
key,
entry,
revision,
generation,
scope_generation,
key_generation,
))
{
self.counters.evictions.fetch_add(1, Ordering::Relaxed);
}
}
drop(store);
if removed > 0 {
release_allocator_memory();
}
}
pub fn configure(&self, settings: Settings) {
*self.tier_settings.lock().unwrap_or_else(|e| e.into_inner()) = settings.clone();
// Hot-applied limit reductions take effect immediately for published HOT
@@ -244,8 +307,7 @@ impl Cache {
let generation = store.generation;
let mut released_hot = false;
while store.entries.len() > settings.max_entries || store.bytes > settings.max_bytes {
let Some((_, key)) = store.lru.first_key_value().map(|(seq, key)| (*seq, *key))
else {
let Some((_, key)) = store.lru.first_key_value().map(|(seq, key)| (*seq, *key)) else {
break;
};
let Some(old) = store.remove(&key) else {