refactor(redis): strengthen shared cache invalidation
This commit is contained in:
@@ -51,7 +51,11 @@ async def cache_get_json(settings: Settings, key: str) -> Any:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None:
|
||||
return None
|
||||
raw = await redis.get(key)
|
||||
try:
|
||||
raw = await redis.get(key)
|
||||
except Exception as exc:
|
||||
logger.warning("Redis cache get failed for key %s: %s", key, exc)
|
||||
return None
|
||||
if raw is None:
|
||||
return None
|
||||
try:
|
||||
@@ -65,14 +69,39 @@ async def cache_set_json(settings: Settings, key: str, value: Any, ttl_seconds:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None:
|
||||
return
|
||||
await redis.set(key, json.dumps(value, ensure_ascii=False, default=str), ex=ttl_seconds)
|
||||
try:
|
||||
await redis.set(key, json.dumps(value, ensure_ascii=False, default=str), ex=ttl_seconds)
|
||||
except Exception as exc:
|
||||
logger.warning("Redis cache set failed for key %s: %s", key, exc)
|
||||
|
||||
|
||||
async def cache_delete(settings: Settings, *keys: str) -> None:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None or not keys:
|
||||
return
|
||||
await redis.delete(*keys)
|
||||
try:
|
||||
await redis.delete(*keys)
|
||||
except Exception as exc:
|
||||
logger.warning("Redis cache delete failed for %s key(s): %s", len(keys), exc)
|
||||
|
||||
|
||||
async def cache_delete_pattern(settings: Settings, pattern: str) -> int:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None or not pattern:
|
||||
return 0
|
||||
deleted = 0
|
||||
batch = []
|
||||
try:
|
||||
async for key in redis.scan_iter(match=pattern, count=100):
|
||||
batch.append(key)
|
||||
if len(batch) >= 100:
|
||||
deleted += int(await redis.delete(*batch))
|
||||
batch.clear()
|
||||
if batch:
|
||||
deleted += int(await redis.delete(*batch))
|
||||
except Exception as exc:
|
||||
logger.warning("Redis cache pattern delete failed for %s: %s", pattern, exc)
|
||||
return deleted
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
|
||||
@@ -24,28 +24,36 @@ async def enqueue_webhook_event(
|
||||
if redis is None:
|
||||
return False
|
||||
|
||||
dedupe_id = event_id or payload.get("id") or payload.get("event_id")
|
||||
if dedupe_id:
|
||||
dedupe_key = redis_key(settings, "webhook", "seen", provider, dedupe_id)
|
||||
if not await redis.set(dedupe_key, "1", nx=True, ex=24 * 60 * 60):
|
||||
logger.info("Skipping duplicate %s webhook event %s", provider, dedupe_id)
|
||||
return True
|
||||
try:
|
||||
dedupe_id = event_id or payload.get("id") or payload.get("event_id")
|
||||
if dedupe_id:
|
||||
dedupe_key = redis_key(settings, "webhook", "seen", provider, dedupe_id)
|
||||
if not await redis.set(dedupe_key, "1", nx=True, ex=24 * 60 * 60):
|
||||
logger.info("Skipping duplicate %s webhook event %s", provider, dedupe_id)
|
||||
return True
|
||||
|
||||
message = {
|
||||
"provider": provider,
|
||||
"event_id": dedupe_id,
|
||||
"payload": payload,
|
||||
"enqueued_at": time.time(),
|
||||
}
|
||||
await redis.lpush(webhook_queue_key(settings), json.dumps(message, ensure_ascii=False))
|
||||
return True
|
||||
message = {
|
||||
"provider": provider,
|
||||
"event_id": dedupe_id,
|
||||
"payload": payload,
|
||||
"enqueued_at": time.time(),
|
||||
}
|
||||
await redis.lpush(webhook_queue_key(settings), json.dumps(message, ensure_ascii=False))
|
||||
return True
|
||||
except Exception as exc:
|
||||
logger.warning("Redis webhook enqueue failed for %s: %s", provider, exc)
|
||||
return False
|
||||
|
||||
|
||||
async def pop_webhook_event(settings: Settings, timeout_seconds: int = 5) -> Optional[dict]:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None:
|
||||
return None
|
||||
item = await redis.brpop(webhook_queue_key(settings), timeout=timeout_seconds)
|
||||
try:
|
||||
item = await redis.brpop(webhook_queue_key(settings), timeout=timeout_seconds)
|
||||
except Exception as exc:
|
||||
logger.warning("Redis webhook pop failed: %s", exc)
|
||||
return None
|
||||
if not item:
|
||||
return None
|
||||
_, raw = item
|
||||
@@ -60,4 +68,8 @@ async def webhook_queue_depth(settings: Settings) -> int:
|
||||
redis = await get_redis(settings)
|
||||
if redis is None:
|
||||
return 0
|
||||
return int(await redis.llen(webhook_queue_key(settings)))
|
||||
try:
|
||||
return int(await redis.llen(webhook_queue_key(settings)))
|
||||
except Exception as exc:
|
||||
logger.warning("Redis webhook queue depth failed: %s", exc)
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user