skip to content

Two Django projects share one Redis database with different KEY_PREFIX values; what does cache.clear() in one project remove, and how do you isolate them properly?

level: seniorimportance: should knowfreq 28%

answer

  1. prefix scopes keys, not commands
  2. what clear() sends to the server
  3. separate databases or servers
  4. delete what you own

basics

~20 s

cache.clear() on RedisCache runs FLUSHDB, so it wipes the whole Redis database, including the other project's keys; KEY_PREFIX does not scope it. Isolate projects with separate Redis databases or servers in LOCATION, and delete known keys instead of clearing.

solid answer

~40 s

`KEY_PREFIX` only changes the keys that `make_key()` builds for per-key calls such as `get`, `set` and `delete`; it is not a namespace the server enforces. `RedisCache.clear()` calls the client's `flushdb()`, the Memcached backends call `flush_all()`, and `DatabaseCache.clear()` runs `DELETE FROM` on its table, so each wipes everything in that store regardless of prefix. On a shared Redis database, a deploy script or test that calls `cache.clear()` logs out or cold-starts the neighbour too. Real isolation means a different `LOCATION`: a separate Redis logical database (`redis://host:6379/1`), a separate server, or a separate table for `DatabaseCache`. Keep `KEY_PREFIX` as well, against accidental key collisions, and replace blanket `clear()` calls with deleting known keys or bumping `VERSION`.

code

python · 17 lines
python
# project A settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://cache.internal:6379/0",
        "KEY_PREFIX": "forecast",
    }
}

# project B settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://cache.internal:6379/1",
        "KEY_PREFIX": "alerts",
    }
}

go deeper

for a junior

Recall that KEY_PREFIX is added to each key Django builds, and that cache.clear() empties a cache.

for a middle

Explain that make_key applies the prefix only to per-key calls, while clear() maps to FLUSHDB, flush_all or a table-wide DELETE.

for a senior

Diagnose the cross-project wipe, isolate stores by LOCATION, and replace routine clear() calls with targeted deletes or VERSION bumps.

for a principal

Treat shared cache infrastructure as a tenancy decision: define which services may share a store and who is allowed to flush it.

## What KEY_PREFIX really does In Django's cache framework, **`KEY_PREFIX`** is applied by `BaseCache.make_key()`, which every per-key operation (`get`, `set`, `add`, `delete`, `get_many`, `incr` and the rest) calls before talking to the server. The default key function turns your key into `prefix:version:key`. That keeps two projects from **reading each other's keys**, but only because both politely build keys through Django. The cache server knows nothing about prefixes. ## What clear() sends to each backend `cache.clear()` does not iterate over "my" keys. It asks the backend to empty the store: | Backend | What `clear()` does | Scope | |---|---|---| | `RedisCache` | client `flushdb()` on the write server | The entire Redis logical database in `LOCATION` | | `PyMemcacheCache` / `PyLibMCCache` | client `flush_all()` | Everything on the Memcached servers | | `DatabaseCache` | `DELETE FROM` the cache table | The whole table | | `FileBasedCache` | removes the cache files in the directory | The whole directory's cache files | | `LocMemCache` | empties the in-process store | That process only | So in the scenario, project A calling `cache.clear()` deletes project B's entries too, even though every B key starts with B's prefix. ## How this bites in practice - A **deploy hook** that runs `cache.clear()` "to be safe" cold-starts every other tenant of the server, and anything they keep in that cache is gone. - A **test suite** pointed at a shared development Redis flushes teammates' or staging data. - A **session store that keeps sessions only in the cache**, on the same database, loses every session, logging users out. (Session engine choice is its own topic; the point here is only that it shares the flushed database.) ## Isolating projects properly 1. **Give each project its own store**: a different Redis logical database number in the URL (`redis://cache:6379/0` versus `/1`), a separate Redis or Memcached server, or a separate `DatabaseCache` table. `clear()` then wipes only that project's store. Memcached has no logical databases, so projects sharing Memcached servers share every `flush_all()`. 2. **Keep a `KEY_PREFIX` anyway** to protect against collisions when stores are shared by accident, for example two environments pointed at one URL. 3. **Stop using `clear()` as routine invalidation**: - delete the specific keys you own when data changes, - bump the alias-wide `VERSION` when a release changes the shape of cached data, so old entries become unreachable without a flush, - keep `clear()` for stores you know are yours alone, such as a test run's `LocMemCache`. 4. **Split aliases by blast radius**: put data that must survive a purge of one area in a different alias with a different `LOCATION`. ## Auditing an existing setup When you inherit a project, three checks find this problem before it finds you: 1. **List every `LOCATION`** across all projects and environments that point at the same cache servers. Two entries with the same Redis URL and database number share one flush scope, whatever their prefixes. 2. **Search the code and scripts for `cache.clear()` and `caches[...].clear()`**, including deploy hooks, management commands, admin actions and test fixtures, and decide for each whether its store is truly private. 3. **Check what else lives in that store**: sessions, rate-limit counters, locks or queues kept by other libraries in the same Redis database are all lost on a flush, and some of them are not caches at all. ## Why this is a senior question The junior model is "prefixes namespace the cache". It is right for reads and writes and wrong for bulk operations, and the gap only shows up in production, when a routine command takes out a neighbour. The senior answer names the exact operation each backend performs, and fixes the problem with separate stores rather than more careful prefixes.

  • Does KEY_PREFIX protect two projects from reading each other's entries?
    Yes for per-key calls made through Django: every `get` and `set` goes through `make_key()`, so project A's `homepage` and project B's `homepage` become different stored keys. It gives no protection against bulk server operations such as `clear()`, or against a client that talks to Redis directly without Django's key function.
  • If both projects must share Memcached servers, how do you retire one project's entries without flush_all()?
    Bump that project's `VERSION` in its `CACHES` entry. Its new keys differ from the old ones, so the old entries are never read and eventually expire or are evicted, while the other project's entries are untouched. Deleting known keys also works for small, known sets.

saying these in an interview costs you the question

  • cache.clear() removes only keys that start with this project's KEY_PREFIX.
  • Redis enforces KEY_PREFIX as a namespace on the server.
  • Calling cache.clear() on every deploy is harmless housekeeping.
  • A different KEY_PREFIX is as strong as a separate Redis database.
  • DatabaseCache.clear() deletes only expired rows.