Feature: policy-lru
Evict the entry that has not been accessed for the longest time.
src/policy/lru.rs uses a pool-based design:
SlotArena<Node<K, V>>— contiguousVecwith a free list for O(1) slot reuse. EachNodestoresprev/nextasOption<SlotId>indices, plus the key andArc<V>.FxHashMap<K, SlotId>— O(1) key-to-slot lookup.head/tail: Option<SlotId>— doubly-linked recency order (head = MRU, tail = LRU).ConcurrentLruCache— thread-safe wrapper viaparking_lot::RwLock.
At steady state (cache full), every insert evicts the tail node (returning its slot to the free list) then inserts the new node (reusing a free slot). No heap allocations occur after the initial warm-up phase.
- Lookup key in map; if present: detach node, attach at head (MRU), return
&Arc<V>.
- If
keyexists: replace value in-place, detach + attach at head. - Else:
- If at capacity:
pop_tail()removes the LRU node from the arena (slot returned to free list), remove its key from the map. arena.insert(Node{...})reuses a free slot or appends.- Insert
SlotIdinto the map, attach at head.
- If at capacity:
- Remove tail node from arena + map, return
(K, Arc<V>).
- Read-only lookup; no list reordering.
- Lookup / update / evict: O(1)
- Memory per entry:
SlotIdprev/next (2 × 12 bytes) + key +Arc<V>+Optiondiscriminant in arena slot - Steady-state allocations: zero (free-list recycling)
Strict global LRU mutates shared metadata on every hit; cachekit provides:
ConcurrentLruCache— singleRwLockaroundLruCore.get()requires a write lock (moves node to head).peek()requires only a read lock (no reordering).- Read-heavy workloads can still contend on
get(); preferpeek()when recency updates are not needed.
- No
unsafe: all list manipulation usesSlotIdindices into the arena. - ABA prevention:
SlotIdincludes a generation counter; stale handles returnNone. - Automatic cleanup:
SlotArenadrops all live nodes whenLruCoreis dropped. Send/Syncauto-derive from the safe field types; no manualunsafe impl.