Problem: ReadCache.evictOldest() in read-cache.ts always deletes the first inserted key (this.store.keys().next().value) — this is FIFO eviction, not LRU. Frequently-accessed entries can be evicted while rarely-accessed entries stay in cache, reducing hit rates under high traffic.
Scope: Replace FIFO eviction with LRU (Least Recently Used) eviction.
Implementation guidance:
On each get() hit, move the entry to the end of the Map (delete and re-insert — JavaScript Map preserves insertion order).
evictOldest() then naturally deletes the least recently used entry (the first key in the Map).
Add a hits and misses counter to ReadCacheStats for observability.
Acceptance criteria: Frequently accessed keys remain in cache under memory pressure; the stats endpoint reports hit/miss counts; all existing read-cache.test.ts tests pass.
Validation: Add a unit test that verifies a recently-accessed key is not evicted when the cache is full.
Files to modify: backend/src/services/read-cache.ts
Problem: ReadCache.evictOldest() in read-cache.ts always deletes the first inserted key (this.store.keys().next().value) — this is FIFO eviction, not LRU. Frequently-accessed entries can be evicted while rarely-accessed entries stay in cache, reducing hit rates under high traffic.
Scope: Replace FIFO eviction with LRU (Least Recently Used) eviction.
Implementation guidance:
On each get() hit, move the entry to the end of the Map (delete and re-insert — JavaScript Map preserves insertion order).
evictOldest() then naturally deletes the least recently used entry (the first key in the Map).
Add a hits and misses counter to ReadCacheStats for observability.
Acceptance criteria: Frequently accessed keys remain in cache under memory pressure; the stats endpoint reports hit/miss counts; all existing read-cache.test.ts tests pass.
Validation: Add a unit test that verifies a recently-accessed key is not evicted when the cache is full.
Files to modify: backend/src/services/read-cache.ts