Skip to content

Cache isolation

Tenants share the cache servers. Without namespacing, two tenants caching the same key (cache.set("dashboard-stats", ...)) read each other's data. tenant_key_func prevents that.

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "KEY_FUNCTION": "django_db_tenants.cache.tenant_key_func",
    }
}

Set it on every cache that tenant code uses.

Key format

Situation Key
No tenant active (control hosts, management commands, tenant_context(None)) <KEY_PREFIX>:<version>:<key>, Django's default, so existing shared keys do not move
Tenant active <KEY_PREFIX>:<version>:@<slug>:<key>
Key starts with django_db_tenants: (the registry's own keys) Django's default format, whatever tenant is active

Slugs cannot contain @, so tenant namespaces never overlap each other or the shared one.

Notes

  • To read or write a deliberately shared key from tenant code, wrap the call in with tenant_context(None):.
  • The cache session backend and anything else built on django.core.cache are namespaced too.
  • cache.clear() still clears the whole backend, for every tenant.

django_db_tenants.cache

Per-tenant cache key namespacing.

Tenants share cache servers, so two tenants caching "dashboard-stats" would read each other's data. Plug :func:tenant_key_func into each cache that tenant code uses::

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "KEY_FUNCTION": "django_db_tenants.cache.tenant_key_func",
    }
}

Keys written while a tenant is active get the tenant's alias in them; keys written with no tenant (control hosts, management commands, tenant_context(None)) keep Django's default format, so turning this on does not move existing shared keys. The library's own registry keys (django_db_tenants:...) are always shared, whichever tenant is active.

cache.clear() still clears the whole backend, for every tenant.

SHARED_KEY_PREFIX module-attribute

SHARED_KEY_PREFIX = 'django_db_tenants:'

tenant_key_func

tenant_key_func(
    key: str, key_prefix: str, version: int | str
) -> str

Django KEY_FUNCTION that namespaces keys by the current tenant.

No tenant: <KEY_PREFIX>:<version>:<key> (Django's default) Tenant active: <KEY_PREFIX>:<version>:@<alias>:<key>

Tenant aliases are slugs, which cannot contain @, so tenant namespaces never overlap each other.