63 lines
2.0 KiB
Python
63 lines
2.0 KiB
Python
"""The namespace hash tag must survive both ways a cache key gets built.
|
|
|
|
cashews format-substitutes `prefix=`, direct construction does not, so the two
|
|
need different spellings of the same tag. If they ever diverge, a write and its
|
|
invalidation land on different keys and nothing raises -- the cache just serves
|
|
stale rows. These tests are what fails instead.
|
|
"""
|
|
|
|
import pytest
|
|
from redis.crc import key_slot
|
|
|
|
from src.cache.client import (
|
|
cache,
|
|
cache_key_namespace,
|
|
cache_prefix_namespace,
|
|
get_cache_namespace,
|
|
)
|
|
from src.crud.session import SESSION_CACHE_KEY_TEMPLATE, session_cache_key
|
|
|
|
|
|
def test_both_spellings_render_the_same_tag():
|
|
ns = get_cache_namespace()
|
|
assert cache_key_namespace() == "{" + ns + "}"
|
|
# Doubled braces collapse to single ones when cashews formats the prefix.
|
|
assert cache_prefix_namespace().format() == cache_key_namespace()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_decorator_key_matches_helper_key():
|
|
"""The key the decorator writes is the key the helper computes."""
|
|
|
|
@cache(
|
|
key=SESSION_CACHE_KEY_TEMPLATE,
|
|
prefix=cache_prefix_namespace(),
|
|
ttl="60s",
|
|
)
|
|
async def get_session(workspace_name: str, session_name: str) -> str:
|
|
# The names matter: cashews fills the key template from them.
|
|
return f"{workspace_name}/{session_name}"
|
|
|
|
await get_session(workspace_name="w1", session_name="s1")
|
|
|
|
written = [k async for k in cache.scan("*")]
|
|
assert session_cache_key("w1", "s1") in written
|
|
|
|
|
|
def test_one_namespace_hashes_to_one_slot():
|
|
"""Every key an instance writes shares a Redis Cluster slot."""
|
|
keys = [
|
|
session_cache_key("w1", "s1"),
|
|
session_cache_key("w2", "s2"),
|
|
f"{cache_key_namespace()}:lock:v2:anything",
|
|
]
|
|
assert len({key_slot(k.encode()) for k in keys}) == 1
|
|
|
|
|
|
def test_namespaces_hash_independently():
|
|
"""Tagging must not collapse the whole fleet onto one shard."""
|
|
slots = {
|
|
key_slot(("{" + n + "}:v2:workspace:w").encode()) for n in ("a1", "b2", "c3")
|
|
}
|
|
assert len(slots) > 1
|