77 lines
2.5 KiB
Python
77 lines
2.5 KiB
Python
from typing import Optional, Dict, Any
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
import asyncio
|
|
|
|
|
|
class MockCacheClient:
|
|
"""Mock cache client that simulates Redis behavior in memory"""
|
|
|
|
def __init__(self):
|
|
self._cache: Dict[str, Dict[str, Any]] = {}
|
|
|
|
async def get(self, key: str) -> Optional[str]:
|
|
"""Get value from cache if not expired"""
|
|
await asyncio.sleep(0.01) # Simulate network delay
|
|
|
|
if key in self._cache:
|
|
entry = self._cache[key]
|
|
if entry['expires_at'] > datetime.utcnow():
|
|
return entry['value']
|
|
else:
|
|
# Remove expired entry
|
|
del self._cache[key]
|
|
return None
|
|
|
|
async def set(self, key: str, value: Any, ttl: int = 86400) -> bool:
|
|
"""Set value in cache with TTL in seconds"""
|
|
await asyncio.sleep(0.01) # Simulate network delay
|
|
|
|
self._cache[key] = {
|
|
'value': json.dumps(value) if not isinstance(value, str) else value,
|
|
'expires_at': datetime.utcnow() + timedelta(seconds=ttl),
|
|
'created_at': datetime.utcnow()
|
|
}
|
|
return True
|
|
|
|
async def setex(self, key: str, ttl: int, value: Any) -> bool:
|
|
"""Set with explicit TTL (Redis compatibility)"""
|
|
return await self.set(key, value, ttl)
|
|
|
|
async def delete(self, key: str) -> bool:
|
|
"""Delete key from cache"""
|
|
if key in self._cache:
|
|
del self._cache[key]
|
|
return True
|
|
return False
|
|
|
|
async def exists(self, key: str) -> bool:
|
|
"""Check if key exists and is not expired"""
|
|
if key in self._cache:
|
|
entry = self._cache[key]
|
|
if entry['expires_at'] > datetime.utcnow():
|
|
return True
|
|
else:
|
|
del self._cache[key]
|
|
return False
|
|
|
|
def clear_all(self):
|
|
"""Clear entire cache (for testing)"""
|
|
self._cache.clear()
|
|
|
|
def get_stats(self) -> Dict[str, Any]:
|
|
"""Get cache statistics"""
|
|
total_keys = len(self._cache)
|
|
expired_keys = sum(
|
|
1 for entry in self._cache.values()
|
|
if entry['expires_at'] <= datetime.utcnow()
|
|
)
|
|
|
|
return {
|
|
'total_keys': total_keys,
|
|
'active_keys': total_keys - expired_keys,
|
|
'expired_keys': expired_keys,
|
|
'cache_size_bytes': sum(
|
|
len(entry['value']) for entry in self._cache.values()
|
|
)
|
|
} |