▮ REFACTORED PATCH
# FIX 1 (the fatal KeyError): create the bucket before use.
# In put(), new-key branch:
- bucket = self.freq_buckets[self.min_freq]
+ bucket = self.freq_buckets.setdefault(self.min_freq, DoublyLinkedListList())
# Same fix in _update_freq and Transaction._update_local_freq (use setdefault).
# FIX 2 (the broken evictor): make it a real async task that deletes:
async def _evict_loop(self, interval):
while True:
await asyncio.sleep(interval) # async, non-blocking
now = time.monotonic()
async with self.global_lock:
expired = [k for k, n in list(self.cache.items()) if now > n.ttl_expiry]
for k in expired:
node = self.cache.pop(k, None)
if node:
self.freq_buckets[node.freq].remove(node) # DELETE, not re-put
async def start_evictor(self, interval=1.0):
self.evictor_task = asyncio.create_task(self._evict_loop(interval))
# FIX 3: delete expired keys; do NOT re-insert with TTL 0.
# FIX 4: time.time() -> time.monotonic().
# FIX 5: add __slots__ to Node / DoublyLinkedListList / ConcurrentLFUCache / Transaction.