Cache Advisor
Analyzes application access patterns, data volatility, and infrastructure to recommend the right caching layer (in-memory, Redis, CDN, HTTP), appropriate TTL policies, and cache invalidation strategies that improve performance without introducing stale data risks.
When to Use
- User asks to "add caching", "reduce DB load", or "speed up this endpoint"
- Database queries are repeated for the same data within short time windows
- API response times are slow due to expensive computations or downstream calls
- User asks about Redis, Memcached, CDN caching, or HTTP cache headers
- Cache invalidation logic is missing or incorrect (stale data issues)
- User asks about cache-aside, write-through, or write-behind patterns
Process
Understand the data characteristics:
- Read frequency: how often is this data read? (per second, per minute)
- Write frequency: how often does it change? (real-time vs. hourly vs. rarely)
- Consistency requirements: is stale data acceptable? For how long?
- Data size: bytes per cached object × expected cardinality = total cache memory
- Sharing: is data per-user (private) or shared (public)?
Choose the appropriate caching layer:
| Layer |
Best For |
Examples |
| In-process (in-memory) |
Same-instance, low-latency, single-node |
lru-cache, Python functools.lru_cache, Go sync.Map |
| Distributed cache |
Multi-instance, session data, shared state |
Redis, Memcached |
| HTTP cache headers |
Public API responses, browser + proxy caching |
Cache-Control, ETag, Last-Modified |
| CDN |
Static assets, geographically distributed reads |
Cloudflare, Fastly, CloudFront |
| Database query cache |
Repeated identical queries |
Redis, query result caching |
Select the caching pattern:
- Cache-Aside (Lazy Loading): check cache → if miss, fetch from source → populate cache → return. Simple, but first request always misses.
- Write-Through: write to cache and DB simultaneously. Cache always current, but write latency is higher.
- Write-Behind (Write-Back): write to cache, async flush to DB. Low write latency, risk of data loss on crash.
- Read-Through: cache sits in front of DB; cache handles misses transparently. Requires cache-DB integration.
- Refresh-Ahead: proactively refresh cache before expiry based on access patterns. Reduces miss rate.
Design the TTL policy:
- Start conservative (shorter TTL) and increase based on observed cache hit rate
- Consider the cost of stale data vs. the cost of a cache miss
- Use sliding TTL (reset on access) for user session data
- Use fixed TTL for time-sensitive data (stock prices, availability)
- Add jitter to TTL (±10%) to prevent cache stampede (all keys expiring simultaneously)
Design the invalidation strategy:
- TTL-based: simplest; accept eventual consistency for the TTL window
- Event-driven: invalidate on write/delete events (database triggers, message queue events)
- Tag-based: group related keys under tags, invalidate the tag to evict all related entries
- Versioned keys:
user:123:v5 — bump version on invalidation; old entries expire naturally
Identify cache stampede risks and add protection:
- Probabilistic Early Expiration (PER) / XFetch algorithm
- Locking: only one process fetches on miss, others wait
- Background refresh before TTL expires
Generate code for the recommended cache implementation.
Output Format
## Caching Recommendation
**Endpoint:** `GET /api/products/:id`
**Current behavior:** Queries PostgreSQL on every request (~45ms avg)
**Read rate:** ~500 req/s · **Write rate:** ~5 updates/hour
### Recommendation: Redis Cache-Aside with 5-minute TTL
**Rationale:** Products change infrequently (5/hour) but are read very frequently
(500 req/s). A 5-minute TTL means at most 5 minutes of stale data — acceptable
for a product catalog. Redis is appropriate since the app runs multiple instances.
### Implementation
```ts
const PRODUCT_TTL = 5 * 60; // 5 minutes in seconds
async function getProduct(id: string): Promise<Product> {
const cacheKey = `product:${id}`;
// Check cache first
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);
// Cache miss — fetch from DB
const product = await db.products.findById(id);
if (!product) return null;
// Populate cache with jitter to prevent stampede
const ttl = PRODUCT_TTL + Math.floor(Math.random() * 30);
await redis.setex(cacheKey, ttl, JSON.stringify(product));
return product;
}
// Invalidate on update
async function updateProduct(id: string, data: Partial<Product>) {
const updated = await db.products.update(id, data);
await redis.del(`product:${id}`); // event-driven invalidation
return updated;
}
Expected impact: 45ms → ~2ms for cached requests (98% cache hit rate expected)
## Examples
### Example Input
We have a user profile endpoint hit 1000 times/second. Profiles change rarely
(user updates their profile maybe once a week). Currently hitting Postgres every time.
### Example Output
Recommendation: In-process LRU cache (primary) + Redis (secondary/shared)
- In-process LRU: 10,000 entries, TTL 60s — serves ~80% of requests with <1ms latency
- Redis fallback: TTL 10 minutes — handles cache misses from in-process eviction
- Invalidation: on profile update, delete from both Redis and broadcast to app instances
via Redis pub/sub to clear in-process caches
At 1000 req/s with 60s TTL, expect >99% hit rate. DB load drops from 1000 QPS to ~10 QPS.
## Boundaries
- Do NOT recommend caching for highly volatile data (financial account balances, inventory counts in checkout flows) without explicitly noting the consistency risk.
- Do NOT recommend in-process caching for multi-instance deployments without noting that each instance has its own cache (no shared state).
- Do NOT cache responses containing user-specific data in a shared (public) cache layer.
- Do NOT recommend turning off cache TTLs (no expiry) without a clearly defined invalidation strategy.
- Always recommend TTL jitter when caching many keys with the same expiry time.
- Do NOT assume Redis is available — if not detected in the project, suggest it as a new dependency and note the operational overhead.
1---2name: cache-advisor3description: Recommends caching strategies (in-memory, Redis, CDN) based on access patterns and suggests TTL policies and invalidation logic. Invoke when asked to add caching, reduce database load, improve API response times, choose a caching strategy, or implement cache invalidation.4---56# Cache Advisor78Analyzes application access patterns, data volatility, and infrastructure to recommend the right caching layer (in-memory, Redis, CDN, HTTP), appropriate TTL policies, and cache invalidation strategies that improve performance without introducing stale data risks.910## When to Use1112- User asks to "add caching", "reduce DB load", or "speed up this endpoint"13- Database queries are repeated for the same data within short time windows14- API response times are slow due to expensive computations or downstream calls15- User asks about Redis, Memcached, CDN caching, or HTTP cache headers16- Cache invalidation logic is missing or incorrect (stale data issues)17- User asks about cache-aside, write-through, or write-behind patterns1819## Process20211. **Understand the data characteristics**:22 - **Read frequency**: how often is this data read? (per second, per minute)23 - **Write frequency**: how often does it change? (real-time vs. hourly vs. rarely)24 - **Consistency requirements**: is stale data acceptable? For how long?25 - **Data size**: bytes per cached object × expected cardinality = total cache memory26 - **Sharing**: is data per-user (private) or shared (public)?27282. **Choose the appropriate caching layer**:2930 | Layer | Best For | Examples |31 |-------|----------|---------|32 | In-process (in-memory) | Same-instance, low-latency, single-node | `lru-cache`, Python `functools.lru_cache`, Go `sync.Map` |33 | Distributed cache | Multi-instance, session data, shared state | Redis, Memcached |34 | HTTP cache headers | Public API responses, browser + proxy caching | `Cache-Control`, `ETag`, `Last-Modified` |35 | CDN | Static assets, geographically distributed reads | Cloudflare, Fastly, CloudFront |36 | Database query cache | Repeated identical queries | Redis, query result caching |37383. **Select the caching pattern**:39 - **Cache-Aside (Lazy Loading)**: check cache → if miss, fetch from source → populate cache → return. Simple, but first request always misses.40 - **Write-Through**: write to cache and DB simultaneously. Cache always current, but write latency is higher.41 - **Write-Behind (Write-Back)**: write to cache, async flush to DB. Low write latency, risk of data loss on crash.42 - **Read-Through**: cache sits in front of DB; cache handles misses transparently. Requires cache-DB integration.43 - **Refresh-Ahead**: proactively refresh cache before expiry based on access patterns. Reduces miss rate.44454. **Design the TTL policy**:46 - Start conservative (shorter TTL) and increase based on observed cache hit rate47 - Consider the cost of stale data vs. the cost of a cache miss48 - Use sliding TTL (reset on access) for user session data49 - Use fixed TTL for time-sensitive data (stock prices, availability)50 - Add jitter to TTL (±10%) to prevent cache stampede (all keys expiring simultaneously)51525. **Design the invalidation strategy**:53 - **TTL-based**: simplest; accept eventual consistency for the TTL window54 - **Event-driven**: invalidate on write/delete events (database triggers, message queue events)55 - **Tag-based**: group related keys under tags, invalidate the tag to evict all related entries56 - **Versioned keys**: `user:123:v5` — bump version on invalidation; old entries expire naturally57586. **Identify cache stampede risks** and add protection:59 - Probabilistic Early Expiration (PER) / XFetch algorithm60 - Locking: only one process fetches on miss, others wait61 - Background refresh before TTL expires62637. **Generate code** for the recommended cache implementation.6465## Output Format6667```68## Caching Recommendation6970**Endpoint:** `GET /api/products/:id`71**Current behavior:** Queries PostgreSQL on every request (~45ms avg)72**Read rate:** ~500 req/s · **Write rate:** ~5 updates/hour7374### Recommendation: Redis Cache-Aside with 5-minute TTL7576**Rationale:** Products change infrequently (5/hour) but are read very frequently77(500 req/s). A 5-minute TTL means at most 5 minutes of stale data — acceptable78for a product catalog. Redis is appropriate since the app runs multiple instances.7980### Implementation81```ts82const PRODUCT_TTL = 5 * 60; // 5 minutes in seconds8384async function getProduct(id: string): Promise<Product> {85 const cacheKey = `product:${id}`;8687 // Check cache first88 const cached = await redis.get(cacheKey);89 if (cached) return JSON.parse(cached);9091 // Cache miss — fetch from DB92 const product = await db.products.findById(id);93 if (!product) return null;9495 // Populate cache with jitter to prevent stampede96 const ttl = PRODUCT_TTL + Math.floor(Math.random() * 30);97 await redis.setex(cacheKey, ttl, JSON.stringify(product));9899 return product;100}101102// Invalidate on update103async function updateProduct(id: string, data: Partial<Product>) {104 const updated = await db.products.update(id, data);105 await redis.del(`product:${id}`); // event-driven invalidation106 return updated;107}108```109110**Expected impact:** ~45ms → ~2ms for cached requests (~98% cache hit rate expected)111```112113## Examples114115### Example Input116```117We have a user profile endpoint hit 1000 times/second. Profiles change rarely118(user updates their profile maybe once a week). Currently hitting Postgres every time.119```120121### Example Output122```123Recommendation: In-process LRU cache (primary) + Redis (secondary/shared)124125- In-process LRU: 10,000 entries, TTL 60s — serves ~80% of requests with <1ms latency126- Redis fallback: TTL 10 minutes — handles cache misses from in-process eviction127- Invalidation: on profile update, delete from both Redis and broadcast to app instances128 via Redis pub/sub to clear in-process caches129130At 1000 req/s with 60s TTL, expect >99% hit rate. DB load drops from 1000 QPS to ~10 QPS.131```132133## Boundaries134135- Do NOT recommend caching for highly volatile data (financial account balances, inventory counts in checkout flows) without explicitly noting the consistency risk.136- Do NOT recommend in-process caching for multi-instance deployments without noting that each instance has its own cache (no shared state).137- Do NOT cache responses containing user-specific data in a shared (public) cache layer.138- Do NOT recommend turning off cache TTLs (no expiry) without a clearly defined invalidation strategy.139- Always recommend TTL jitter when caching many keys with the same expiry time.140- Do NOT assume Redis is available — if not detected in the project, suggest it as a new dependency and note the operational overhead.