// Check value size before storingconst value = JSON.stringify(data);
if (value.length > 100000) {
console.warn('Large value detected:', value.length, 'bytes');
}
Missing indexes - use the right data structure:
TypeScript
// ❌ Slow - scanning all usersconst users = await redis.hgetall('users');
const activeUsers = Object.entries(users).filter(([_, u]) => u.active);
// ✅ Fast - separate set for active usersawait redis.sadd('users:active', visitorId);
const activeUsers = await redis.smembers('users:active');
// See all commands in real-time (don't use in production!)const monitor = await redis.monitor();
monitor.on('monitor', (time, args) => {
console.log(time, args);
});
Troubleshooting
Solutions to common issues with Redis connections and performance.
Connection Issues#
"ECONNREFUSED" or "Connection refused"#
Cause: Can't reach the Redis server.
Solutions:
// Make sure you're using the correct URL console.log('Connecting to:', process.env.ARCTICKEY_URL?.replace(/:[^:@]+@/, ':***@'));# Test if you can reach the host nc -zv your-db.arctickey.com 6379Check for firewall rules blocking port 6379
Ensure TLS is enabled - use
rediss://notredis://"NOAUTH Authentication required"#
Cause: Missing or incorrect password.
Solutions:
rediss://default:YOUR_PASSWORD@host:6379Verify the password is correct in your dashboard
Check for special characters that need URL encoding:
// If password contains special chars, encode it const password = encodeURIComponent('pass@word!'); const url = `rediss://default:${password}@host:6379`;"WRONGPASS invalid username-password pair"#
Cause: Password is incorrect or has been rotated.
Solutions:
"Connection timeout"#
Cause: Slow network or connection pool exhausted.
Solutions:
const redis = new Redis({ ...options, connectTimeout: 10000, // 10 seconds });// For ioredis const redis = new Redis({ ...options, maxRetriesPerRequest: 3, retryStrategy(times) { return Math.min(times * 50, 2000); }, });"TLS/SSL handshake failed"#
Cause: TLS configuration issue.
Solutions:
rediss://URL scheme:// ✅ Correct const url = 'rediss://default:pass@host:6379'; // ❌ Wrong const url = 'redis://default:pass@host:6379';const redis = new Redis({ ...options, tls: { rejectUnauthorized: false, // Only for testing! }, });Performance Issues#
Slow commands#
Diagnosis:
// Log slow commands redis.on('ready', () => { redis.config('SET', 'slowlog-log-slower-than', 10000); // 10ms }); // Check slow log const slowLog = await redis.slowlog('GET', 10); console.log(slowLog);Common causes:
// ❌ Slow - blocks server const keys = await redis.keys('user:*'); // ✅ Fast - iterates without blocking let cursor = '0'; const keys = []; do { const [nextCursor, batch] = await redis.scan(cursor, 'MATCH', 'user:*', 'COUNT', 100); cursor = nextCursor; keys.push(...batch); } while (cursor !== '0');// Check value size before storing const value = JSON.stringify(data); if (value.length > 100000) { console.warn('Large value detected:', value.length, 'bytes'); }// ❌ Slow - scanning all users const users = await redis.hgetall('users'); const activeUsers = Object.entries(users).filter(([_, u]) => u.active); // ✅ Fast - separate set for active users await redis.sadd('users:active', visitorId); const activeUsers = await redis.smembers('users:active');High memory usage#
Diagnosis:
const info = await redis.info('memory'); console.log(info); // Look for: used_memory_human, maxmemorySolutions:
// Always set expiry await redis.setex('key', 3600, 'value'); // Or add to existing keys await redis.expire('key', 3600);# Using redis-cli redis-cli --bigkeys// Store object fields, not huge JSON // ❌ Less efficient await redis.set('user:123', JSON.stringify({ name: 'Alice', email: '...', ... })); // ✅ More efficient for partial reads await redis.hset('user:123', { name: 'Alice', email: '...' });import { gzip, gunzip } from 'zlib'; import { promisify } from 'util'; const compress = promisify(gzip); const decompress = promisify(gunzip); // Store compressed const compressed = await compress(JSON.stringify(largeData)); await redis.set('key', compressed.toString('base64')); // Retrieve and decompress const data = await redis.get('key'); const decompressed = await decompress(Buffer.from(data, 'base64')); const parsed = JSON.parse(decompressed.toString());Too many connections#
Cause: Creating new connections instead of reusing.
Solutions:
// lib/redis.ts import Redis from 'ioredis'; let redis: Redis | null = null; export function getRedis() { if (!redis) { redis = new Redis(process.env.ARCTICKEY_URL); } return redis; }// Vercel/Lambda - connection per invocation import Redis from 'ioredis'; const redis = new Redis(process.env.ARCTICKEY_URL, { maxRetriesPerRequest: 1, lazyConnect: true, }); export async function handler() { await redis.connect(); // ... use redis await redis.quit(); }Error Messages#
"OOM command not allowed"#
Cause: Memory limit reached.
Solutions:
"BUSY Redis is busy running a script"#
Cause: Long-running Lua script blocking server.
Solutions:
SCRIPT KILLto stop runaway scripts"READONLY You can't write against a read only replica"#
Cause: Writing to a read replica.
Solutions:
Debugging Tips#
Log all commands#
const redis = new Redis(process.env.ARCTICKEY_URL); redis.on('connect', () => console.log('Redis connected')); redis.on('error', (err) => console.error('Redis error:', err)); redis.on('close', () => console.log('Redis connection closed')); // Log all commands (development only!) if (process.env.NODE_ENV === 'development') { const originalSendCommand = redis.sendCommand.bind(redis); redis.sendCommand = (command) => { console.log('Redis:', command.name, command.args); return originalSendCommand(command); }; }Check connection status#
console.log('Status:', redis.status); // 'connecting', 'connect', 'ready', 'close', 'reconnecting', 'end' const ping = await redis.ping(); console.log('Ping:', ping); // 'PONG'Monitor in real-time#
// See all commands in real-time (don't use in production!) const monitor = await redis.monitor(); monitor.on('monitor', (time, args) => { console.log(time, args); });Getting Help#
If you're still stuck: