Skip to content

Cache Issues

Solutions for common cache problems.

Problem: Cache Not Working

Symptoms

  • Always hitting database
  • Cache hit rate is 0%
  • @Cached decorator not caching

Diagnosis

bash
# Check if keys exist
redis-cli KEYS "cache:*"

# Check specific key
redis-cli GET "cache:user:123"

# Check TTL
redis-cli TTL "cache:user:123"

Solutions

CauseSolution
Plugin not registeredAdd CachePlugin to plugins array
Wrong key patternCheck key template matches arguments
TTL = 0Set positive TTL value
Redis not connectedCheck connection settings
typescript
// @Cached uses positional placeholders: {0}, {1}, etc.
@Cached({ key: 'user:{0}', ttl: 3600 })
async getUser(id: string) { }
typescript
// Named placeholders like {userId} are not resolved by @Cached
@Cached({ key: 'user:{userId}', ttl: 3600 })
async getUser(id: string) { }

Problem: Stale Data

Symptoms

  • Updated data not appearing
  • Old values returned after update
  • Inconsistency between instances

Diagnosis

bash
# Check when key was set
redis-cli OBJECT IDLETIME "cache:user:123"

# Check TTL remaining
redis-cli TTL "cache:user:123"

Solutions

CauseSolution
No invalidationAdd @CacheEvict on updates
Wrong invalidation keyVerify key matches cached key
L1 not invalidatedEnable pub/sub for L1 sync
Tags not matchingCheck tag names match
typescript
// Cache
@Cached({
  key: 'user:{0}',
  tags: (id) => ['users', `user:${id}`],
})
async getUser(id: string) { }

// Invalidate - tags must match!
async updateUser(id: string, data: any) {
  await this.userRepository.update(id, data);
  await this.cache.invalidateTags([`user:${id}`, 'users']);
}

Problem: Low Hit Rate

Symptoms

  • Hit rate below 80%
  • High database load
  • Cache seems ineffective

Diagnosis

yaml
# Check hit rate
sum(rate(redisx_cache_hits_total[5m])) / 
(sum(rate(redisx_cache_hits_total[5m])) + sum(rate(redisx_cache_misses_total[5m])))

Solutions

CauseSolution
TTL too shortIncrease TTL
Key too specificGeneralize key pattern
Low repeat requestsConsider if caching is appropriate
Cache evictionsIncrease Redis memory
typescript
// Reusable across requests
@Cached({ key: 'search:{0}', ttl: 60 })
typescript
// Too specific - every request is unique
@Cached({ key: 'search:{0}:{1}', ttl: 60 })

Problem: Cache Stampede

Symptoms

  • Database overwhelmed on cache miss
  • Multiple identical queries simultaneously
  • Spike after cache expiration

Solutions

typescript
// Enable stampede protection
@Cached({
  key: 'popular:item',
  ttl: 300,
  stampede: {
    enabled: true,
    lockTimeout: 5000,
  },
})
async getPopularItem() { }

Or use stale-while-revalidate:

typescript
@Cached({
  key: 'dashboard',
  ttl: 60,
  swr: true,
  staleTime: 300, // Serve stale for 5 min while refreshing
})
async getDashboard() { }

Problem: Memory Issues

Symptoms

  • Redis memory maxed out
  • Evictions increasing
  • OOM errors

Diagnosis

bash
# Check memory usage
redis-cli INFO memory

# Check eviction policy
redis-cli CONFIG GET maxmemory-policy

# Find large keys
redis-cli --bigkeys

Solutions

CauseSolution
TTL too longReduce TTL
Large valuesCompress or reduce data
Too many keysIncrease memory or reduce cardinality
No eviction policySet maxmemory-policy to allkeys-lru

Problem: Tags Not Invalidating

Symptoms

  • invalidateTags called but data not cleared
  • Some keys cleared, others not
  • Tag invalidation slow

Diagnosis

bash
# Check tag index
redis-cli SMEMBERS "tag:users"

# Check if key has tag
redis-cli GET "cache:user:123"

Solutions

CauseSolution
Tag name mismatchVerify exact tag names
Tags not indexedEnsure tags were set on cache
Pub/sub not workingCheck Redis pub/sub
typescript
// Setting tags — use function form for dynamic values
@Cached({
  key: 'user:{0}',
  tags: (id) => ['users', `user:${id}`],
})
async getUser(id: string) { }

// Invalidating — tags must match exactly
await cache.invalidateTags(['users']);  // Clears all users
await cache.invalidateTags(['user:123']);  // Clears specific user

Next Steps

Released under the MIT License.