fix(prop-grid-rs): evict expired IdxCache entries on get
The in-memory idx cache kept entries around forever: get() returned None on expiry but left the HashMap entry in place. Each hourly HRRR cycle adds ~38 new idx URLs (19 forecast hours × surface + pressure), so the map grew ~4 MiB/day indefinitely on each Rust replica. Not the main cause of the OOM loop (glibc fragmentation was; jemalloc landed last commit) but worth sealing so a long- lived pod's idx cache can't drift toward the 3 GiB ceiling over weeks. Evicts on-touch via `get()` — simple, amortized, no background sweep thread needed.
This commit is contained in:
parent
641789449a
commit
3ec976ccc4
1 changed files with 37 additions and 1 deletions
|
|
@ -294,11 +294,14 @@ impl IdxCache {
|
|||
}
|
||||
|
||||
pub fn get(&self, url: &str) -> Option<String> {
|
||||
if let Ok(map) = self.inner.lock() {
|
||||
if let Ok(mut map) = self.inner.lock() {
|
||||
if let Some(entry) = map.get(url) {
|
||||
if entry.inserted.elapsed() < IDX_TTL {
|
||||
return Some(entry.body.clone());
|
||||
}
|
||||
// Expired entry — evict so unique idx URLs don't
|
||||
// accumulate across hourly HRRR cycles.
|
||||
map.remove(url);
|
||||
}
|
||||
}
|
||||
if let Some(disk) = &self.disk {
|
||||
|
|
@ -332,6 +335,24 @@ impl IdxCache {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn len(&self) -> usize {
|
||||
self.inner.lock().map(|m| m.len()).unwrap_or(0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn put_with_age(&self, url: String, body: String, age: Duration) {
|
||||
if let Ok(mut map) = self.inner.lock() {
|
||||
map.insert(
|
||||
url,
|
||||
IdxCacheEntry {
|
||||
body,
|
||||
inserted: Instant::now() - age,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── HTTP client ──────────────────────────────────────────────────────
|
||||
|
|
@ -722,6 +743,21 @@ mod tests {
|
|||
assert!(cache.get("missing").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idx_cache_removes_expired_entries_on_get() {
|
||||
// Without eviction the HashMap grows forever as unique idx URLs
|
||||
// accumulate each hour. Calling get() on an expired entry must
|
||||
// remove it so the in-memory map bounds itself to the hot set.
|
||||
let cache = IdxCache::new();
|
||||
cache.put_with_age("stale".into(), "old".into(), IDX_TTL + Duration::from_secs(1));
|
||||
cache.put("fresh".into(), "new".into());
|
||||
|
||||
assert_eq!(cache.len(), 2);
|
||||
assert!(cache.get("stale").is_none());
|
||||
assert_eq!(cache.len(), 1, "expired entry should have been evicted");
|
||||
assert_eq!(cache.get("fresh").as_deref(), Some("new"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disk_idx_cache_round_trips() {
|
||||
let tmp = tempfile::TempDir::new().unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue