CORE-4.2: Create a Singleton Redis Service
Ticket ID: CORE-4.2
Milestone: 4 - High-Performance Services Layer
Priority: 🔴 Critical
Status: Not Started
Description
Create a file at /lib/redis.ts. In this file, initialize a new Redis client and export it as a singleton instance. This ensures that only one connection pool is created and shared efficiently across the application.
Context
A singleton Redis client is essential for performance and resource management. According to the High-Level Architecture, Redis handles:
- Session Storage: User sessions and authentication tokens
- Real-time Data: Current campaign status, deliverability metrics
- Rate Limiting: API rate limiting and abuse prevention
- Queue Processing: Fast job queues for email processing
A singleton pattern ensures efficient connection reuse and prevents connection pool exhaustion.
Acceptance Criteria
- ✅ The
/lib/redis.tsfile exists and exports a single client instance - ✅ The application can import and use this client instance in any server-side module without errors
- ✅ The Redis client is initialized with the
REDIS_URLfrom environment variables - ✅ The client handles connection errors gracefully
- ✅ The client supports basic operations (get, set, ping)
- ✅ Multiple imports of the Redis client use the same instance (singleton pattern)
Technical Details
Redis Service Structure
// lib/redis.ts
import Redis from 'ioredis';
let redis: Redis | null = null;
export function getRedisClient(): Redis {
if (!redis) {
redis = new Redis(process.env.REDIS_URL!, {
maxRetriesPerRequest: 3,
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000);
return delay;
},
reconnectOnError: (err) => {
const targetError = 'READONLY';
if (err.message.includes(targetError)) {
return true;
}
return false;
},
});
redis.on('error', (err) => {
console.error('Redis Client Error:', err);
});
redis.on('connect', () => {
console.log('Redis Client Connected');
});
}
return redis;
}
// Export singleton instance
export const redis = getRedisClient();
Alternative: Direct Singleton Export
// lib/redis.ts
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!, {
maxRetriesPerRequest: 3,
// ... other options
});
redis.on('error', (err) => {
console.error('Redis Client Error:', err);
});
export { redis };
Implementation Notes
- Use singleton pattern to ensure only one connection is created
- Configure connection retry strategy for resilience
- Add error handling and logging
- Consider adding connection health monitoring
- Support both development (local) and production (cloud) Redis instances
- Configure appropriate timeout and retry settings
Related Documentation
- High-Level Architecture - Cache layer
- Database Schema Guide - Queue system architecture (Redis integration)
- Queue System Implementation Guide - Queue system design and Redis integration
- Connection Pooling Strategy - Database connection management
Dependencies
- CORE-4.1 - Redis client must be installed and configured
Testing
- Verify Redis client can be imported without errors
- Test that multiple imports use the same instance
- Test basic Redis operations (get, set, ping)
- Verify connection is established successfully
- Test error handling for invalid connection string
- Test error handling for Redis server unavailable
- Verify connection retry logic works
Related Documentation
- Routes: core-app-structure
- API: API Reference