Review multi-tenant cache keys before merge
Cursor or Claude Code applies a SKILL.md on cache PRs: tenant id in every key, shared global entries, and locks that cross customers — with merge-blocking findings.
A cache-isolation SKILL.md for data paths, a PR comment listing key templates per cache, and no lookup merged that can serve one tenant's payload to another.
- 1
Require tenant id in the key helper
A function that accepts an optional tenant id will be called without one. Make the argument required for tenant-scoped namespaces.
- 2
Review one permissions cache and one public cache
The skill should Block the first if the key omits the organization id, and allow the second only when the comment says global.
- 3
Pair with the RLS review on schema PRs
A new tenant column in Postgres still needs matching cache keys. Link both skills from the data PR template.
The prompt to paste
Write SKILL.md for reviewing application cache keys before merge. Focus on tenant isolation in Redis, Memcached, in-process maps, and CDN cache keys. This is not a Postgres RLS policy review. Repository context: [cache clients in use and their key helpers] [tenant identifier: organization_id, tenant_id, or workspace id — the column name this repo uses] [what is cached: permissions, billing flags, query results, rendered HTML — list the real ones] [TTL policy for auth decisions, if one exists] PR context (paste when using): [PR link, new or edited cache get/set sites, key template if the author wrote one] SKILL.md must include: 1. Purpose — a key without the tenant id returns another customer's permissions, query rows, or rendered page. 2. When to use — any PR that adds or changes a cache get, set, delete, or lock. When not to use — SQL policy changes with no cache (RLS review), or TTL-only tweaks on a key that already includes the tenant id. 3. Hard rules for findings: - Block: tenant-scoped value stored under a key that omits the tenant id; key built only from a user-supplied string (email, slug, search query) that can collide across tenants; authorization or feature-flag cache shared globally; lock or single-flight key without the tenant id so one customer repopulates another's entry; cache delete-by-prefix that wipes every tenant. - Warn: TTL on permission or billing entries longer than the team's revocation window; value blob that embeds a tenant id the key does not; CDN or edge cache keyed only on URL when the response varies by tenant cookie; in-process Map used as a cross-request cache in a multi-tenant server without a tenant prefix. - Note: key separator style (: vs /) — never block merge on naming alone when the tenant id is present and unambiguous. 4. Review steps the agent must execute: - List each get/set pair touched by the diff. Write the key template with placeholders, including tenant id or the explicit word global. - For each template, name the value type and whether it contains another tenant's data if the id were wrong. - Check invalidation: logout, role change, and tenant deletion must drop or version the matching keys. - Check locks and single-flight wrappers use the same tenant-scoped template as the value. - If a cache is intentionally global (public marketing page), the comment must say so. Silence is not global-by-default. 5. PR comment template: key table (site — template — tenant component — TTL — invalidation), findings, merge recommendation. 6. Safe patterns section: helper that requires a tenant id argument, version suffix bumped when the shape changes, separate key namespace for truly global data. 7. Two example prompts: cache a per-organization billing flag vs cache a public changelog that is the same for every visitor. 8. Pitfalls: prefixing with user id and assuming one user has one tenant, hashing the full SQL string without the tenant predicate, flushing the whole Redis DB in a migration, testing with a single local tenant so collisions never show. 9. Done means: every Block resolved, every new key template written in the comment, no production key dumps or customer payloads in the review. Rules: - Do not invent cache sites that are not in the diff or repository context. - Do not suggest disabling the cache in production to prove isolation. - Output only SKILL.md markdown.
A single-flight lock keyed only by query text lets one tenant's result fill the cache entry other tenants wait on. The skill must Block locks that are wider than the value key.
Sample case written by Hidden Logic Lab. Not affiliated with Cursor or Claude Code.
More in Data
Get new cases in your inbox
Weekly notes on new use cases and agent workflows. No product pitches.