---
name: caching-patterns
description: Caching strategies and patterns for GoodGo microservices including multi-layer cache, Redis caching, cache key naming, TTL strategies, cache invalidation, and cache-aside patterns.
---

# Caching Patterns

## When to Use This Skill

Use this skill when:
- Implementing caching for frequently accessed data
- Optimizing database queries with caching
- Designing cache key naming conventions
- Setting TTL (Time To Live) strategies
- Implementing cache invalidation patterns
- Using multi-layer cache (L1: Memory, L2: Redis)
- Handling cache failures gracefully

## Core Concepts

### Multi-Layer Cache Strategy

The platform uses a two-layer cache architecture to balance speed and capacity:

```mermaid
graph TB
    subgraph Application["Application Layer"]
        App[Application Code]
    end
    
    subgraph L1Layer["L1 Cache - Memory (NodeCache)"]
        L1[In-Memory Cache]
        L1Props["• Speed: < 1ms<br/>• Capacity: 10k keys<br/>• TTL: 60s-5min<br/>• Scope: Per-instance"]
    end
    
    subgraph L2Layer["L2 Cache - Redis (Distributed)"]
        L2[Redis Cache]
        L2Props["• Speed: < 5ms<br/>• Capacity: Large<br/>• TTL: Configurable<br/>• Scope: Shared"]
    end
    
    subgraph DataLayer["Data Source"]
        DB[(Database)]
        API[External API]
    end
    
    App -->|Check First| L1
    L1 -->|Miss| L2
    L2 -->|Miss| DB
    L2 -->|Miss| API
    DB -->|Store| L2
    API -->|Store| L2
    L2 -->|Warm| L1
    
    L1 -.-> L1Props
    L2 -.-> L2Props
    
    style L1 fill:#e1f5ff
    style L2 fill:#fff4e1
    style DB fill:#ffe1e1
    style API fill:#ffe1e1
```

**Layer Characteristics:**

1. **L1 Cache (Memory)**: NodeCache in-memory cache
   - Very fast (< 1ms access time)
   - Limited capacity (10k keys default)
   - Short TTL (60 seconds default, max 5 minutes)
   - Per-instance (not shared across instances)

2. **L2 Cache (Redis)**: Distributed Redis cache
   - Fast (< 5ms access time)
   - Large capacity
   - Longer TTL (configurable)
   - Shared across all service instances

### Cache Flow

The cache lookup follows a multi-layer approach, checking L1 first, then L2, and finally the data source.

```mermaid
flowchart TD
    Start([Request Data]) --> CheckL1{Check L1 Cache<br/>Memory}
    CheckL1 -->|Hit| ReturnL1[Return Data<br/>from L1]
    CheckL1 -->|Miss| CheckL2{Check L2 Cache<br/>Redis}
    CheckL2 -->|Hit| StoreL1[Store in L1<br/>Warm Cache]
    StoreL1 --> ReturnL2[Return Data<br/>from L2]
    CheckL2 -->|Miss| FetchSource[Fetch from<br/>Data Source]
    FetchSource --> StoreBoth[Store in L1 & L2]
    StoreBoth --> ReturnSource[Return Data<br/>from Source]
    
    ReturnL1 --> End([End])
    ReturnL2 --> End
    ReturnSource --> End
    
    style CheckL1 fill:#e1f5ff
    style CheckL2 fill:#fff4e1
    style FetchSource fill:#ffe1e1
    style ReturnL1 fill:#e1ffe1
    style ReturnL2 fill:#e1ffe1
    style ReturnSource fill:#e1ffe1
```

## Patterns

### Cache Service Usage

```typescript
import { cacheService } from '../core/cache';

// Simple get/set
const cached = await cacheService.get<User>('user:123');
await cacheService.set('user:123', userData, 300); // 5 minutes TTL

// Get or set pattern (cache-aside)
const user = await cacheService.getOrSet(
  'user:123',
  async () => {
    return await userRepository.findById('123');
  },
  300 // TTL in seconds
);
```

### Cache Key Naming Conventions

Use consistent naming patterns:

```typescript
// Pattern: {entity}:{identifier}
'user:123'
'user:email:user@example.com'
'user:123:permissions'
'user:123:roles'

// Pattern: {entity}:{identifier}:{sub-resource}
'session:abc123'
'permission:perm_123'
'role:role_123'
```

Cache service provides key generators:

```typescript
cacheService.keys = {
  user: (userId: string) => `user:${userId}`,
  userPermissions: (userId: string) => `user:${userId}:permissions`,
  userRoles: (userId: string) => `user:${userId}:roles`,
  token: (token: string) => `token:${token}`,
  session: (sessionId: string) => `session:${sessionId}`,
};
```

### Cache-Aside Pattern

Most common pattern - check cache first, fetch if miss:

```typescript
async getUserPermissions(userId: string): Promise<string[]> {
  const cacheKey = cacheService.keys.userPermissions(userId);
  
  // Try cache first
  const cached = await cacheService.get<string[]>(cacheKey);
  if (cached) {
    return cached;
  }
  
  // Cache miss - fetch from source
  const permissions = await calculatePermissions(userId);
  
  // Store in cache
  await cacheService.set(cacheKey, permissions, 300); // 5 min TTL
  
  return permissions;
}
```

### Get or Set Pattern

Simplified cache-aside pattern:

```typescript
const permissions = await cacheService.getOrSet(
  cacheService.keys.userPermissions(userId),
  async () => {
    // This function only runs on cache miss
    return await calculatePermissions(userId);
  },
  300 // TTL
);
```

### TTL Strategies

Choose TTL based on data characteristics:

**Short TTL (60-300s)**: Frequently changing data
- User permissions (300s)
- Session data (varies)
- Real-time statistics

**Medium TTL (300-1800s)**: Moderately changing data
- User profiles (600s)
- Organization data (900s)
- Configuration (1800s)

**Long TTL (1800-3600s)**: Rarely changing data
- Static configurations (3600s)
- Reference data (7200s)

**No TTL**: Very stable data (use with caution)
- Rarely use - prefer long TTL instead

### Cache Invalidation

Invalidate cache when data changes to prevent serving stale data. The platform supports multiple invalidation strategies:

```mermaid
flowchart TD
    Start([Data Changed]) --> ChooseStrategy{Choose<br/>Invalidation<br/>Strategy}
    
    ChooseStrategy -->|Single Key| SingleKey[Single Key<br/>Invalidation]
    SingleKey --> DelL1[Delete from L1]
    DelL1 --> DelL2[Delete from L2]
    DelL2 --> Done1([Complete])
    
    ChooseStrategy -->|Pattern Match| PatternMatch[Pattern-Based<br/>Invalidation]
    PatternMatch --> FindKeys[Find Matching Keys<br/>user:123:*]
    FindKeys --> DelManyL1[Delete from L1<br/>All Matching]
    DelManyL1 --> DelManyL2[Delete from L2<br/>All Matching]
    DelManyL2 --> Done2([Complete])
    
    ChooseStrategy -->|Multiple Keys| MultipleKeys[Multiple Keys<br/>Invalidation]
    MultipleKeys --> ListKeys[List Keys to Delete<br/>user:123<br/>user:123:permissions<br/>user:123:roles]
    ListKeys --> BatchDelL1[Batch Delete from L1]
    BatchDelL1 --> BatchDelL2[Batch Delete from L2]
    BatchDelL2 --> Done3([Complete])
    
    style SingleKey fill:#e1f5ff
    style PatternMatch fill:#fff4e1
    style MultipleKeys fill:#ffe1e1
    style Done1 fill:#e1ffe1
    style Done2 fill:#e1ffe1
    style Done3 fill:#e1ffe1
```

**Implementation Examples:**

```typescript
// Single key invalidation
await cacheService.del(cacheService.keys.user(userId));
await cacheService.del(cacheService.keys.userPermissions(userId));

// Pattern-based invalidation
await cacheService.invalidatePattern('user:123:*');

// Multiple keys
await cacheService.delMany([
  cacheService.keys.user(userId),
  cacheService.keys.userPermissions(userId),
  cacheService.keys.userRoles(userId),
]);
```

### Cache Warming

Pre-populate cache with frequently accessed data:

```typescript
async warmCache() {
  const activeUsers = await userRepository.findActiveUsers();
  
  for (const user of activeUsers) {
    // Pre-cache user data
    await cacheService.set(
      cacheService.keys.user(user.id),
      user,
      600
    );
    
    // Pre-cache permissions
    const permissions = await calculatePermissions(user.id);
    await cacheService.set(
      cacheService.keys.userPermissions(user.id),
      permissions,
      300
    );
  }
}
```

### Error Handling

Cache failures should not break the application:

```typescript
async getWithCache(key: string): Promise<Data | null> {
  try {
    // Try cache first
    const cached = await cacheService.get<Data>(key);
    if (cached) return cached;
  } catch (error) {
    // Log but continue - fallback to source
    logger.warn('Cache get failed, falling back to source', { key, error });
  }
  
  // Fallback to data source
  return await fetchFromSource();
}
```

## Best Practices

1. **Cache Keys**: Use consistent naming conventions
2. **TTL Selection**: Choose TTL based on data change frequency
3. **Invalidation**: Invalidate cache when data changes
4. **Error Handling**: Don't let cache failures break the app
5. **Cache-Aside**: Use cache-aside pattern for most cases
6. **Avoid Over-Caching**: Don't cache data that changes too frequently
7. **Monitor Hit Rates**: Track cache hit rates to optimize TTL
8. **Warm Cache**: Pre-populate cache for critical data
9. **Use Multi-Layer**: Leverage both L1 and L2 cache
10. **Serialize Properly**: Ensure data is JSON serializable

## Common Mistakes

1. **Cache Key Collisions**: Using generic keys that collide
2. **Stale Data**: Not invalidating cache when data changes
3. **Too Short TTL**: Setting TTL too short, negating cache benefits
4. **Too Long TTL**: Setting TTL too long, serving stale data
5. **No Error Handling**: Letting cache errors break the application
6. **Caching Everything**: Caching data that doesn't benefit from caching
7. **Not Warming Cache**: Not pre-populating critical cache data
8. **Ignoring Hit Rates**: Not monitoring cache performance

## Troubleshooting

### Low Cache Hit Rate

**Problem**: Cache hit rate is low, cache not effective
**Solution**: 
- Review TTL values - may be too short
- Check cache key patterns - ensure consistent usage
- Verify cache invalidation isn't too aggressive
- Monitor what data is being cached

### Stale Data Issues

**Problem**: Serving stale data from cache
**Solution**:
- Review TTL values - may be too long
- Ensure cache invalidation on data updates
- Use shorter TTL for frequently changing data
- Implement cache versioning if needed

### Cache Performance Issues

**Problem**: Cache operations are slow
**Solution**:
- Check Redis connection and network latency
- Monitor Redis memory usage
- Review cache key patterns for efficiency
- Consider L1 cache hit rate (should be high)

## Resources

- [Multi-Layer Cache](../../services/iam-service/src/core/cache/multi-layer-cache.ts) - Multi-layer cache implementation
- [Cache Service](../../services/iam-service/src/core/cache/cache.service.ts) - Cache service wrapper
- [Cache Usage Example](../../services/iam-service/src/modules/rbac/rbac.service.ts) - Real-world cache usage
