Learning outcome
You will be able to design cache keys that are safe for multi-tenant systems, explain why cache lookups must never be treated as authorization, and describe the cache-aside read pattern clearly in interview terms. By the end, you should be able to look at a product-catalog endpoint and decide what belongs in the key, what should stay out of the cache, and where the real security boundary lives.
Intuition
Caching is about saving work after you already know what data is allowed to be served. Authorization is about deciding whether the caller may see the data at all. Those are different responsibilities.
A common interview trap is to blur them together: “If the item is missing from cache, deny access.” That is incorrect. A cache miss only means the cache does not currently hold a value. It says nothing about whether the user is allowed to load the value from the database or another source of truth.
For a product catalog, a public product page might be safe to cache broadly, while a tenant-specific price, contract discount, or hidden inventory flag must be isolated. If two tenants can see different effective data, then the cache key must encode the boundary that changes the answer.
A simple mental model helps:
request -> authorize -> build safe cache key -> read cache -> if miss, load source -> store -> return
The key idea is that the authorization decision happens before the cache lookup is used to satisfy the request. The cache is a performance layer, not a security layer.
Deep dive
Cache-aside means the application controls the read path explicitly. On a read request, the application first checks the cache. If the value is present, it returns the cached value. If the value is absent, the application loads the canonical data from the database or service, then populates the cache, then returns the data.
For interviews, describe it this way: the application is responsible for keeping the cache warm on demand.
The tricky part is designing the key. A good key reflects every dimension that can change the result. In a multi-tenant product catalog, that might include:
- tenant ID, if product visibility or pricing differs by tenant
- user role or entitlements, if the response depends on permissions
- locale, if translated text is cached
- currency or region, if prices are rendered differently
- product ID or SKU, of course
A bad key omits one of those dimensions. Then one caller can receive another caller’s cached response. That is a data leak, even if the underlying database access is properly protected.
For example, suppose tenant A has a negotiated price of $80 and tenant B sees the standard price of $100. If the cache key is only product:42, the first tenant to request the product may populate the cache with the wrong price for the other tenant. The fix is not “add a permission check inside cache.” The fix is to make the key tenant-aware, such as tenant:A:product:42:price. If the user role also changes the answer, include it too.
Keep in mind the difference between public and private data. Public product descriptions might be safe to share across tenants, so a broader key could be acceptable. Private data should be scoped tightly. The point is not to make every key as specific as possible; it is to make each key as specific as the data variance requires.
In practice, use a stable key format and treat it as part of your API contract. That makes reasoning and invalidation easier. A common pattern is a namespaced prefix:
catalog:v1:tenant:{tenantId}:product:{productId}
The v1 portion helps when schema or shaping changes. If you change the payload structure, a versioned namespace lets you avoid mixing old and new shapes.
Also remember that caching is only about the read path. On writes, the source of truth remains the database or service. After a product update, you might invalidate related keys or write through an updated value, depending on the design. But even then, the cache does not become the authority; it just mirrors data that the application has already decided is valid.
Another useful interview distinction is between in-process and distributed caching. An in-process cache is local to one application instance, so it is fast but not shared. A distributed cache is shared across instances, which helps scale out, but it is still just a cache unless you explicitly build stronger coordination on top. Do not assume a distributed cache gives you locking, consensus, or correctness guarantees for writes.
Failure modes
Here are the most common mistakes interviewers expect you to catch:
- Using a too-generic key
- Example:
product:42 - Risk: cross-tenant or cross-user leakage
- Putting authorization inside the cache lookup
- Risk: a cache miss is not a deny decision
- Correct approach: authorize first, then use cache as an optimization
- Caching user-specific or tenant-specific data under a shared key
- Risk: one caller receives another caller’s response
- Assuming the cache is the source of truth
- Risk: stale or partial data becomes authoritative by accident
- Ignoring schema evolution
- Risk: old cache entries collide with new response shapes
- Assuming distributed cache means distributed locking
- Risk: concurrent writers or refreshers still race unless you add explicit coordination
A good failure timeline to explain in an interview:
T1: tenant A requests product 42 -> cache miss -> load price 80 -> store under product:42 T2: tenant B requests product 42 -> cache hit -> receives price 80, but should see 100
That timeline shows why the cache key, not just the cached value, must respect tenant boundaries.
Interview drill
Use these questions to practice concise answers.
1) Why is cache-aside not an authorization mechanism?
- Correct answer: because authorization decides whether the caller may access data, while cache-aside only decides whether to load from cache or the source on a read.
- Worked explanation: a cache miss may simply mean the data is cold. The application must still enforce access rules before returning any data.
2) What should be part of a cache key in a multi-tenant product system?
- Correct answer: any dimension that changes the returned value, such as tenant ID, product ID, locale, currency, or entitlement tier.
- Worked explanation: if two callers can legitimately see different responses, they must not share a key.
3) When is it acceptable to share a cache entry across tenants?
- Correct answer: only when the cached response is truly identical and safe for all of them, such as a public product description with no tenant-specific shaping.
- Worked explanation: shared caching is fine for shared data, but private data must be isolated.
4) Does a distributed cache automatically prevent race conditions?
- Correct answer: no.
- Worked explanation: a distributed cache shares storage, not necessarily coordination semantics. It can still need explicit locking, versioning, or invalidation design.
Revision checklist
- I can explain cache-aside as “read cache, on miss load source, then populate cache.”
- I can state clearly that caching is an optimization, not an authorization layer.
- I can identify which request dimensions belong in a cache key.
- I can spot cross-tenant leakage caused by an overly broad key.
- I can explain why a cache miss must not be interpreted as access denied.
- I can distinguish in-process caching from distributed caching.
- I can mention that distributed caching does not automatically provide distributed locking.
- I can describe a failure timeline where two tenants receive each other’s data because of a bad key.