stock-bot/docs/enhanced-cache-usage.md

3.7 KiB

Enhanced Cache Provider Usage

The Redis cache provider now supports advanced TTL handling and conditional operations.

Basic Usage (Backward Compatible)

import { RedisCache } from '@stock-bot/cache';

const cache = new RedisCache({
  keyPrefix: 'trading:',
  defaultTTL: 3600 // 1 hour
});

// Simple set with TTL (old way - still works)
await cache.set('user:123', userData, 1800); // 30 minutes

// Simple get
const user = await cache.get<UserData>('user:123');

Enhanced Set Options

// Preserve existing TTL when updating
await cache.set('user:123', updatedUserData, { preserveTTL: true });

// Only set if key exists (update operation)
const oldValue = await cache.set('user:123', newData, { 
  onlyIfExists: true, 
  getOldValue: true 
});

// Only set if key doesn't exist (create operation)
await cache.set('user:456', newUser, { 
  onlyIfNotExists: true,
  ttl: 7200 // 2 hours
});

// Get old value when setting new one
const previousData = await cache.set('session:abc', sessionData, { 
  getOldValue: true,
  ttl: 1800
});

Convenience Methods

// Update value preserving TTL
await cache.update('user:123', updatedUserData);

// Set only if exists
const updated = await cache.setIfExists('user:123', newData, 3600);

// Set only if not exists (returns true if created)
const created = await cache.setIfNotExists('user:456', userData);

// Replace existing key with new TTL
const oldData = await cache.replace('user:123', newData, 7200);

// Atomic field updates
await cache.updateField('counter:views', (current) => (current || 0) + 1);

await cache.updateField('user:123', (user) => ({
  ...user,
  lastSeen: new Date().toISOString(),
  loginCount: (user?.loginCount || 0) + 1
}));

Stock Bot Use Cases

1. Rate Limiting

// Only create rate limit if not exists
const rateLimited = await cache.setIfNotExists(
  `ratelimit:${userId}:${endpoint}`, 
  { count: 1, resetTime: Date.now() + 60000 },
  60 // 1 minute
);

if (!rateLimited) {
  // Increment existing counter
  await cache.updateField(`ratelimit:${userId}:${endpoint}`, (data) => ({
    ...data,
    count: data.count + 1
  }));
}

2. Session Management

// Update session data without changing expiration
await cache.update(`session:${sessionId}`, {
  ...sessionData,
  lastActivity: Date.now()
});

3. Cache Warming

// Only update existing cached data, don't create new entries
const warmed = await cache.setIfExists(`stock:${symbol}:price`, latestPrice);
if (warmed) {
  console.log(`Warmed cache for ${symbol}`);
}

4. Atomic Counters

// Thread-safe counter increments
await cache.updateField('metrics:api:calls', (count) => (count || 0) + 1);
await cache.updateField('metrics:errors:500', (count) => (count || 0) + 1);

5. TTL Preservation for Frequently Updated Data

// Keep original expiration when updating frequently changing data
await cache.set(`portfolio:${userId}:positions`, positions, { preserveTTL: true });

Error Handling

The cache provider includes robust error handling:

try {
  await cache.set('key', value);
} catch (error) {
  // Errors are logged and fallback values returned
  // The cache operations are non-blocking
}

// Check cache health
const isHealthy = await cache.health();

// Wait for cache to be ready
await cache.waitForReady(10000); // 10 second timeout

Performance Benefits

  1. Atomic Operations: updateField uses Lua scripts to prevent race conditions
  2. TTL Preservation: Avoids unnecessary TTL resets on updates
  3. Conditional Operations: Reduces network round trips
  4. Shared Connections: Efficient connection pooling
  5. Error Recovery: Graceful degradation when Redis is unavailable