▮ RECOMMENDED USE
Strong architectural instincts for async data-structure design but unsafe to ship — needs fixes to token corruption, O(1) freq maintenance, and the sync/async transaction boundary before use.
▮ REFACTORED PATCH
# Fix 1 (line 223): remove the stray space in the attribute access
- self._ bump_freq(node)
+ self._bump_freq(node)
# Fix 2: O(1) min_freq update — never rescan. On bump, when the old bucket
# empties and it WAS the min, advance min_freq by 1 (the node just moved to freq+1).
def _bump_freq(self, node):
old_freq = node.freq
self._remove_from_freq_list(node)
node.freq += 1
self._add_to_freq_list(node)
if old_freq == self._min_freq and self._freq_map[old_freq].is_empty():
del self._freq_map[old_freq]
self._min_freq += 1
# Fix 3: make Transaction.get async and await the cache call
async def get(self, key):
if self._committed or self._rolled_back:
raise RuntimeError("Transaction already closed")
if key in self._deletes: return None
if key in self._writes:
value, expires_at = self._writes[key]
if expires_at > 0 and time.monotonic() >= expires_at:
self._deletes.add(key); return None
return value
return await self._cache.get(key)
# Fix 4 (stop_evictor): await the cancelled task, don't run_until_complete
async def stop_evictor(self):
if self._evictor_task is not None:
self._evictor_task.cancel()
try: await self._evictor_task
except asyncio.CancelledError: pass
self._evictor_task = None
# Fix 5: add slots to _Node (Python 3.10+)
@dataclass(slots=True)