fix(rust): evict in-memory cache entries at the expiry boundary

Matches Python evict_cache, which uses expiration <= now

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Yujong Lee 2026-09-16 18:30:45 +00:00
parent fc93c9ccba
commit 44a518ea25
2 changed files with 19 additions and 1 deletions

View file

@ -154,7 +154,7 @@ impl<V: Clone> InMemoryCache<V> {
while let Some(Reverse((expiration, key))) = state.expiration_heap.peek().cloned() {
if state.expirations.get(&key).copied() != Some(expiration) {
state.expiration_heap.pop();
} else if expiration < now {
} else if expiration <= now {
state.expiration_heap.pop();
Self::remove(state, &key);
} else {

View file

@ -45,6 +45,24 @@ fn default_explicit_and_override_ttls_follow_python_rules(clock: Arc<AtomicU64>)
);
}
#[rstest]
fn write_at_expiry_boundary_refreshes_ttl(clock: Arc<AtomicU64>) {
let cache = cache(clock.clone(), 4);
cache
.set_cache("key", "first".into(), Some(Duration::from_secs(10)))
.unwrap();
clock.store(110, Ordering::SeqCst);
cache
.set_cache("key", "second".into(), Some(Duration::from_secs(10)))
.unwrap();
assert_eq!(
cache.expires_at("key").unwrap(),
Some(Duration::from_secs(120))
);
clock.store(115, Ordering::SeqCst);
assert_eq!(cache.get_cache("key").unwrap(), Some("second".into()));
}
#[rstest]
fn capacity_evicts_earliest_and_ignores_stale_heap_entries(clock: Arc<AtomicU64>) {
let cache = cache(clock, 2);