diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1fdb784..431f37c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## v0.15.0 (TBD) +- Fixed `FifoCache::push` creating a ghost eviction entry when called with a key that already exists in the cache. The duplicate queue entry inflated the eviction queue length beyond the number of live map entries, consumed an eviction slot without a corresponding map value, and caused a still-live entry to be prematurely dropped when the ghost surfaced as the oldest key. Existing keys are now updated in-place without modifying the eviction queue. + - Added `ca-certificates` to the node Docker runtime image so outbound `https` connections work in containerized deployments ([#1661](https://github.com/0xMiden/node/issues/1661)). - Reworked `SyncNotes` store queries to fetch multiple matching blocks within one database transaction while preserving the response payload cap ([#2027](https://github.com/0xMiden/node/pull/2027)). - Added composite index `idx_transactions_account_block_txid` on `transactions(account_id, block_num, transaction_id)` to speed up `select_transactions_records` queries used by `SyncTransactions` ([#1965](https://github.com/0xMiden/node/issues/1965)). diff --git a/crates/utils/src/fifo_cache.rs b/crates/utils/src/fifo_cache.rs index 148f06be9..7bff20db3 100644 --- a/crates/utils/src/fifo_cache.rs +++ b/crates/utils/src/fifo_cache.rs @@ -39,8 +39,20 @@ where } /// Inserts a key-value pair, evicting the oldest entry if the cache is at capacity. + /// Inserts or updates `key` with `value`. + /// + /// If `key` already exists, its value is updated in-place without touching the eviction + /// queue. Re-enqueueing an existing key would create a ghost entry: the queue would + /// grow beyond the number of live map entries, consume an eviction slot for a key that + /// may no longer exist, and prematurely evict a valid entry when the ghost surfaces as + /// the oldest key. pub fn push(&self, key: K, value: V) { let mut inner = self.0.lock().expect("fifo cache lock poisoned"); + // Key already in cache: update value in-place, leave eviction queue unchanged. + if inner.map.contains_key(&key) { + inner.map.insert(key, value); + return; + } if inner.eviction.len() >= inner.capacity.get() { if let Some(oldest) = inner.eviction.pop_front() { inner.map.remove(&oldest);