Fix remaining cache issues: index TTL, sub-index cleanup, top-level imports
- RedisCache.put: add pipe.expire() on index sets matching entry TTL, prevents orphaned index entries when cache values expire - Broad purge: delete_indexes_by_prefix() cleans per-param sub-indexes (mizan:idx:ctx:k=v) that previously leaked as dead sets - Move cache imports to top of executor.py (were inline in view functions) - Update KNOWN_ISSUES.md — all 16 issues now resolved or documented Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -172,6 +172,9 @@ def cache_purge(
|
||||
keys_to_delete = backend.get_index(index_key)
|
||||
backend.delete_index(index_key)
|
||||
|
||||
# Clean up per-param sub-indexes (e.g., mizan:idx:user:user_id=5)
|
||||
backend.delete_indexes_by_prefix(f"mizan:idx:{context}:")
|
||||
|
||||
if keys_to_delete:
|
||||
return backend.delete_many(list(keys_to_delete))
|
||||
return 0
|
||||
|
||||
20
packages/mizan-django/src/mizan/cache/backend.py
vendored
20
packages/mizan-django/src/mizan/cache/backend.py
vendored
@@ -20,6 +20,7 @@ class CacheBackend(Protocol):
|
||||
def get_index(self, index_key: str) -> set[str]: ...
|
||||
def remove_from_index(self, index_key: str, members: set[str]) -> None: ...
|
||||
def delete_index(self, index_key: str) -> None: ...
|
||||
def delete_indexes_by_prefix(self, prefix: str) -> None: ...
|
||||
def clear(self) -> None: ...
|
||||
|
||||
|
||||
@@ -65,6 +66,11 @@ class MemoryCache:
|
||||
def delete_index(self, index_key: str) -> None:
|
||||
self._indexes.pop(index_key, None)
|
||||
|
||||
def delete_indexes_by_prefix(self, prefix: str) -> None:
|
||||
to_delete = [k for k in self._indexes if k.startswith(prefix)]
|
||||
for k in to_delete:
|
||||
del self._indexes[k]
|
||||
|
||||
def clear(self) -> None:
|
||||
self._store.clear()
|
||||
self._indexes.clear()
|
||||
@@ -116,6 +122,7 @@ class RedisCache:
|
||||
pipe.set(prefixed_key, value, ex=self._ttl)
|
||||
for idx in indexes:
|
||||
pipe.sadd(self._key(idx), key)
|
||||
pipe.expire(self._key(idx), self._ttl)
|
||||
pipe.execute()
|
||||
|
||||
def delete_many(self, keys: list[str]) -> int:
|
||||
@@ -135,6 +142,19 @@ class RedisCache:
|
||||
def delete_index(self, index_key: str) -> None:
|
||||
self._client.delete(self._key(index_key))
|
||||
|
||||
def delete_indexes_by_prefix(self, prefix: str) -> None:
|
||||
pattern = f"{self._prefix}{prefix}*"
|
||||
cursor = 0
|
||||
while True:
|
||||
cursor, keys = self._client.scan(cursor, match=pattern, count=100)
|
||||
if keys:
|
||||
pipe = self._client.pipeline()
|
||||
for key in keys:
|
||||
pipe.unlink(key)
|
||||
pipe.execute()
|
||||
if cursor == 0:
|
||||
break
|
||||
|
||||
def clear(self) -> None:
|
||||
pattern = f"{self._prefix}*"
|
||||
cursor = 0
|
||||
|
||||
@@ -27,7 +27,9 @@ from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
from mizan.cache import get_cache, cache_get, cache_put, cache_purge
|
||||
from mizan.setup.registry import get_function, get_context_groups
|
||||
from mizan.setup.settings import get_settings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
@@ -662,8 +664,6 @@ def function_call_view(request: HttpRequest) -> JsonResponse:
|
||||
response["X-Mizan-Invalidate"] = _format_invalidate_header(invalidate_contexts)
|
||||
|
||||
# Purge origin-side cache for invalidated contexts
|
||||
import logging
|
||||
from mizan.cache import get_cache, cache_purge
|
||||
_cache_log = logging.getLogger("mizan.cache")
|
||||
cache = get_cache()
|
||||
if cache is not None:
|
||||
@@ -776,9 +776,6 @@ def context_fetch_view(request: HttpRequest, context_name: str) -> JsonResponse:
|
||||
params = request.GET.dict()
|
||||
|
||||
# Origin-side cache lookup
|
||||
import logging
|
||||
from mizan.cache import get_cache, cache_get, cache_put
|
||||
from mizan.setup.settings import get_settings
|
||||
_cache_log = logging.getLogger("mizan.cache")
|
||||
cache = get_cache()
|
||||
cache_settings = get_settings()
|
||||
|
||||
Reference in New Issue
Block a user