class RedisCache:
__slots__ = ('_prefix', '_redis')
def __init__(
self, redis: Redis, *, prefix: str = 'oxidehttp:cache:'
) -> None:
self._redis = redis
self._prefix = prefix.rstrip(':')
def _key(self, key: str) -> str:
return f'{self._prefix}:{key}' if self._prefix else key
async def get(self, key: str) -> CachedResponse | None:
data = await self._redis.get(self._key(key))
if data is None:
return None
payload = decode(data)
return CachedResponse(
_status_code=HTTPStatus(payload['status_code']),
_headers=payload['headers'],
data=bytes.fromhex(payload['body']),
)
async def set(
self,
key: str,
status_code: HTTPStatus,
headers: Headers,
data: bytes | memoryview,
ttl: float,
) -> None:
if ttl <= 0:
return
payload = {
'status_code': status_code,
'headers': dict(headers),
'body': data.hex(),
}
data = encode(payload)
await self._redis.set(self._key(key), data, px=max(1, int(ttl * 1000)))
async def delete(self, key: str) -> None:
await self._redis.delete(self._key(key))
async def clear(self) -> None:
pattern = f'{self._prefix}*'
cursor = 0
while True:
cursor, keys = await self._redis.scan(
cursor=cursor, match=pattern, count=100
)
if keys:
await self._redis.delete(*keys)
if cursor == 0:
break