▮ REFACTORED PATCH
# 1. Track min_freq incrementally and prune empty buckets in _update_freq/_delete_internal
def _update_freq(self, key):
old = self._key_to_freq[key]; new = old + 1
self._key_to_freq[key] = new
old_b = self._freq_buckets[old]; del old_b[key]
if not old_b and old == self._min_freq: # prune + advance min_freq
del self._freq_buckets[old]; self._min_freq = new
elif not old_b:
del self._freq_buckets[old]
self._freq_buckets.setdefault(new, OrderedDict())[key] = None
def _evict_lfu(self):
bucket = self._freq_buckets.get(self._min_freq)
if not bucket: return
victim = next(iter(bucket)) # oldest in min-freq bucket = LRU tie-break
self._delete_internal(victim)
# 2. On new insertion set self._min_freq = 1 (after 0->1 bump).
# 3. Fix Transaction read-through: make get async and await the cache.
async def get(self, key):
if self._rolled_back: raise RuntimeError('rolled back')
if key in self._deletes: return None
if key in self._writes: return self._writes[key].value
return await self.cache.get(key) # was: self.cache.get(key) -> coroutine
# 4. Evictor must hold the lock: drop the ThreadPoolExecutor; just:
async def _evictor_loop(self):
while self._evictor_running:
await asyncio.sleep(1.0)
async with self._lock: self._cleanup_expired()
# 5. Replace time.time() with time.monotonic() everywhere; add __slots__.