42 lines
1.1 KiB
Python
42 lines
1.1 KiB
Python
|
|
"""Simple async-safe TTL cache."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
import time
|
||
|
|
from typing import Any, Dict, Optional, Tuple
|
||
|
|
|
||
|
|
|
||
|
|
class TTLCache:
|
||
|
|
"""Thread/async-safe in-memory cache with per-key TTL."""
|
||
|
|
|
||
|
|
def __init__(self) -> None:
|
||
|
|
self._store: Dict[str, Tuple[Any, float]] = {}
|
||
|
|
self._lock = asyncio.Lock()
|
||
|
|
|
||
|
|
async def get(self, key: str) -> Optional[Any]:
|
||
|
|
async with self._lock:
|
||
|
|
entry = self._store.get(key)
|
||
|
|
if entry is None:
|
||
|
|
return None
|
||
|
|
value, expires_at = entry
|
||
|
|
if time.time() > expires_at:
|
||
|
|
del self._store[key]
|
||
|
|
return None
|
||
|
|
return value
|
||
|
|
|
||
|
|
async def set(self, key: str, value: Any, ttl: int) -> None:
|
||
|
|
async with self._lock:
|
||
|
|
self._store[key] = (value, time.time() + ttl)
|
||
|
|
|
||
|
|
async def invalidate(self, key: str) -> None:
|
||
|
|
async with self._lock:
|
||
|
|
self._store.pop(key, None)
|
||
|
|
|
||
|
|
async def clear(self) -> None:
|
||
|
|
async with self._lock:
|
||
|
|
self._store.clear()
|
||
|
|
|
||
|
|
|
||
|
|
cache = TTLCache()
|