fix(redis_cache): apply namespace in delete_cache and async_delete_cache

When a Redis namespace is configured, SET and GET correctly prefix the
key via check_and_fix_namespace, but DELETE did not — causing the
PodLockManager to always target a non-existent key and never explicitly
release locks (they would expire by TTL instead, with a warning on every cycle).

Adds check_and_fix_namespace to both async_delete_cache and delete_cache,
and covers both paths with a parametrized unit test (namespace=None / "myns").
This commit is contained in:
Stanislav Kostenko 2026-04-29 19:40:30 +03:00 committed by Stanislav Kostenko
parent 3e1479c052
commit 62473fc4ff
2 changed files with 29 additions and 3 deletions

View file

@ -1259,10 +1259,11 @@ class RedisCache(BaseCache):
async def async_delete_cache(self, key: str):
# typed as Any, redis python lib has incomplete type stubs for RedisCluster and does not include `delete`
_redis_client: Any = self.init_async_client()
# keys is str
key = self.check_and_fix_namespace(key=key)
return await _redis_client.delete(key)
def delete_cache(self, key):
def delete_cache(self, key: str):
key = self.check_and_fix_namespace(key=key)
self.redis_client.delete(key)
async def _pipeline_increment_helper(

View file

@ -3,7 +3,6 @@ import sys
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
sys.path.insert(
0, os.path.abspath("../../..")
@ -473,3 +472,29 @@ async def test_async_lpop_with_float_redis_version(
# Verify the method completed without error
assert result is not None
@pytest.mark.parametrize("namespace", [None, "myns"])
@pytest.mark.asyncio
async def test_delete_cache_applies_namespace(namespace, monkeypatch, redis_no_ping):
"""async_delete_cache and delete_cache must apply the namespace prefix so that
DEL targets the same key that SET/GET use."""
monkeypatch.setenv("REDIS_HOST", "https://my-test-host")
redis_cache = RedisCache(namespace=namespace)
raw_key = "cronjob_lock:db_spend_update_job"
expected_key = f"{namespace}:{raw_key}" if namespace else raw_key
# --- async path ---
mock_async_client = AsyncMock()
with patch.object(
redis_cache, "init_async_client", return_value=mock_async_client
):
await redis_cache.async_delete_cache(key=raw_key)
mock_async_client.delete.assert_called_once_with(expected_key)
# --- sync path ---
mock_sync_client = MagicMock()
redis_cache.redis_client = mock_sync_client
redis_cache.delete_cache(key=raw_key)
mock_sync_client.delete.assert_called_once_with(expected_key)