refactor: replace OrderedDict with plain dict and remove async from DAL cache methods and invalidation helpers

This commit is contained in:
2025-11-29 11:40:49 +00:00
parent 137a8e1232
commit 5bdabe63a1
2 changed files with 78 additions and 84 deletions
+58 -64
View File
@@ -24,53 +24,51 @@ class CacheEntry:
class DALCache:
def __init__(self, maxsize: int = 50000):
self.maxsize = maxsize
self._data: OrderedDict[str, CacheEntry] = OrderedDict()
self._data: Dict[str, CacheEntry] = {}
self._lock = asyncio.Lock()
self._stats = {"hits": 0, "misses": 0, "evictions": 0}
self._hits = 0
self._misses = 0
async def get(self, key: str) -> Optional[Any]:
async with self._lock:
if key in self._data:
entry = self._data[key]
if entry.is_expired:
del self._data[key]
self._stats["misses"] += 1
return None
entry.access_count += 1
self._data.move_to_end(key)
self._stats["hits"] += 1
return entry.value
self._stats["misses"] += 1
return None
def get(self, key: str) -> Optional[Any]:
entry = self._data.get(key)
if entry is not None:
if entry.is_expired:
self._data.pop(key, None)
self._misses += 1
return None
entry.access_count += 1
self._hits += 1
return entry.value
self._misses += 1
return None
async def set(self, key: str, value: Any, ttl: float = 300.0):
async with self._lock:
if len(self._data) >= self.maxsize:
oldest_key = next(iter(self._data))
del self._data[oldest_key]
self._stats["evictions"] += 1
self._data[key] = CacheEntry(value=value, ttl=ttl)
if len(self._data) >= self.maxsize:
async with self._lock:
if len(self._data) >= self.maxsize:
keys = list(self._data.keys())[:1000]
for k in keys:
self._data.pop(k, None)
self._data[key] = CacheEntry(value=value, ttl=ttl)
async def delete(self, key: str):
async with self._lock:
self._data.pop(key, None)
def delete(self, key: str):
self._data.pop(key, None)
async def invalidate_prefix(self, prefix: str) -> int:
async with self._lock:
keys_to_delete = [k for k in self._data.keys() if k.startswith(prefix)]
for k in keys_to_delete:
del self._data[k]
return len(keys_to_delete)
def invalidate_prefix(self, prefix: str) -> int:
keys_to_delete = [k for k in self._data.keys() if k.startswith(prefix)]
for k in keys_to_delete:
self._data.pop(k, None)
return len(keys_to_delete)
async def clear(self):
async with self._lock:
self._data.clear()
def clear(self):
self._data.clear()
def get_stats(self) -> Dict[str, Any]:
total = self._stats["hits"] + self._stats["misses"]
hit_rate = (self._stats["hits"] / total * 100) if total > 0 else 0
total = self._hits + self._misses
hit_rate = (self._hits / total * 100) if total > 0 else 0
return {
**self._stats,
"hits": self._hits,
"misses": self._misses,
"size": len(self._data),
"hit_rate": round(hit_rate, 2),
}
@@ -85,16 +83,12 @@ class DataAccessLayer:
def __init__(self, cache_size: int = 50000):
self._cache = DALCache(maxsize=cache_size)
self._path_cache: Dict[str, Tuple[Any, Any, bool]] = {}
self._path_lock = asyncio.Lock()
async def start(self):
logger.info("DataAccessLayer started")
async def stop(self):
await self._cache.clear()
async with self._path_lock:
self._path_cache.clear()
self._cache.clear()
logger.info("DataAccessLayer stopped")
def _user_key(self, user_id: int) -> str:
@@ -118,7 +112,7 @@ class DataAccessLayer:
async def get_user_by_id(self, user_id: int) -> Optional[Any]:
key = self._user_key(user_id)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -131,7 +125,7 @@ class DataAccessLayer:
async def get_user_by_username(self, username: str) -> Optional[Any]:
key = self._user_by_name_key(username)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -143,10 +137,10 @@ class DataAccessLayer:
return user
async def invalidate_user(self, user_id: int, username: Optional[str] = None):
await self._cache.delete(self._user_key(user_id))
self._cache.delete(self._user_key(user_id))
if username:
await self._cache.delete(self._user_by_name_key(username))
await self._cache.invalidate_prefix(f"u:{user_id}:")
self._cache.delete(self._user_by_name_key(username))
self._cache.invalidate_prefix(f"u:{user_id}:")
async def refresh_user_cache(self, user):
await self._cache.set(self._user_key(user.id), user, ttl=self.TTL_USER)
@@ -161,7 +155,7 @@ class DataAccessLayer:
) -> Optional[Any]:
if folder_id:
key = self._folder_key(user_id, folder_id)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -189,7 +183,7 @@ class DataAccessLayer:
) -> Optional[Any]:
if file_id:
key = self._file_key(user_id, file_id)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -214,7 +208,7 @@ class DataAccessLayer:
folder_id: Optional[int] = None,
) -> Tuple[List[Any], List[Any]]:
key = self._folder_contents_key(user_id, folder_id)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -241,7 +235,7 @@ class DataAccessLayer:
return None, None, True
key = self._path_key(user_id, path_str)
cached = await self._cache.get(key)
cached = self._cache.get(key)
if cached is not None:
return cached
@@ -293,24 +287,24 @@ class DataAccessLayer:
await self._cache.set(key, result, ttl=self.TTL_PATH)
return result
async def invalidate_folder(self, user_id: int, folder_id: Optional[int] = None):
await self._cache.delete(self._folder_key(user_id, folder_id))
await self._cache.delete(self._folder_contents_key(user_id, folder_id))
await self._cache.invalidate_prefix(f"u:{user_id}:path:")
def invalidate_folder(self, user_id: int, folder_id: Optional[int] = None):
self._cache.delete(self._folder_key(user_id, folder_id))
self._cache.delete(self._folder_contents_key(user_id, folder_id))
self._cache.invalidate_prefix(f"u:{user_id}:path:")
async def invalidate_file(self, user_id: int, file_id: int, parent_id: Optional[int] = None):
await self._cache.delete(self._file_key(user_id, file_id))
def invalidate_file(self, user_id: int, file_id: int, parent_id: Optional[int] = None):
self._cache.delete(self._file_key(user_id, file_id))
if parent_id is not None:
await self._cache.delete(self._folder_contents_key(user_id, parent_id))
await self._cache.invalidate_prefix(f"u:{user_id}:path:")
self._cache.delete(self._folder_contents_key(user_id, parent_id))
self._cache.invalidate_prefix(f"u:{user_id}:path:")
async def invalidate_path(self, user_id: int, path: str):
def invalidate_path(self, user_id: int, path: str):
key = self._path_key(user_id, path)
await self._cache.delete(key)
self._cache.delete(key)
async def invalidate_user_paths(self, user_id: int):
await self._cache.invalidate_prefix(f"u:{user_id}:path:")
await self._cache.invalidate_prefix(f"u:{user_id}:contents:")
def invalidate_user_paths(self, user_id: int):
self._cache.invalidate_prefix(f"u:{user_id}:path:")
self._cache.invalidate_prefix(f"u:{user_id}:contents:")
def get_stats(self) -> Dict[str, Any]:
return self._cache.get_stats()