A robust, performance-focused and full-featured Redis client for Node.js with TypeScript support, clustering, sentinel management, and comprehensive Redis command coverage.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Redis Cluster client provides automatic slot management, node discovery, failover handling, and load distribution across multiple Redis nodes. It maintains compatibility with the Redis client API while adding cluster-specific functionality.
Main client class for Redis Cluster deployments with automatic slot mapping and node management.
class Cluster extends EventEmitter {
constructor(startupNodes: ClusterNode[], options?: ClusterOptions);
// Connection management
connect(): Promise<void>;
disconnect(reconnect?: boolean): void;
quit(callback?: Callback<"OK">): Promise<"OK">;
duplicate(overrideStartupNodes?: ClusterNode[], overrideOptions?: ClusterOptions): Cluster;
// Properties
readonly options: ClusterOptions;
readonly status: ClusterStatus;
readonly autoPipelineQueueSize: number;
// Cluster management
nodes(role?: NodeRole): Redis[];
refreshSlotsCache(callback?: Callback<void>): void;
// All Redis commands are available with automatic slot routing
}
type ClusterNode = string | number | {
host?: string;
port?: number;
};
type ClusterStatus = "wait" | "reconnecting" | "connecting" | "connect" | "ready" | "close" | "end";
type NodeRole = "master" | "slave" | "all";Usage Examples:
import { Cluster } from "ioredis";
// Basic cluster connection
const cluster = new Cluster([
{ host: "127.0.0.1", port: 7000 },
{ host: "127.0.0.1", port: 7001 },
{ host: "127.0.0.1", port: 7002 }
]);
// Connection with options
const cluster2 = new Cluster([
"127.0.0.1:7000",
"127.0.0.1:7001",
"127.0.0.1:7002"
], {
scaleReads: "slave",
maxRedirections: 16,
retryStrategy: (times) => Math.min(times * 50, 2000)
});
// Automatic Redis command routing
await cluster.set("key", "value");
const value = await cluster.get("key");Access and manage individual Redis nodes within the cluster.
// Get nodes by role
nodes(role?: NodeRole): Redis[];
// Refresh slot mapping from cluster
refreshSlotsCache(callback?: Callback<void>): void;Usage Examples:
// Get all master nodes
const masters = cluster.nodes("master");
console.log(`${masters.length} master nodes`);
// Get all slave nodes
const slaves = cluster.nodes("slave");
// Get all nodes
const allNodes = cluster.nodes("all");
// Manual slot cache refresh
cluster.refreshSlotsCache((err) => {
if (err) console.error("Failed to refresh slots:", err);
else console.log("Slots cache refreshed");
});Comprehensive configuration options extending Redis options with cluster-specific settings.
interface ClusterOptions extends Partial<RedisOptions> {
// Cluster behavior
clusterRetryStrategy?: (times: number, reason?: Error) => number | void | null;
enableOfflineQueue?: boolean;
redisOptions?: RedisOptions;
scaleReads?: NodeRole | ((node: any, command: any) => Redis);
// Slot management
maxRedirections?: number;
retryDelayOnClusterDown?: number;
retryDelayOnMove?: number;
retryDelayOnFailover?: number;
// Node discovery
enableReadyCheck?: boolean;
lazyConnect?: boolean;
dnsLookup?: DNSLookupFunction;
// Performance
enableAutoPipelining?: boolean;
autoPipeliningIgnoredCommands?: string[];
// NAT support
natMap?: NatMap;
// Custom slot calculation
keyHashTag?: string;
// Cluster commands
customCommandHandlers?: Record<string, (command: string, args: any[]) => void>;
}
interface NatMap {
[key: string]: { host: string; port: number };
}Usage Examples:
const cluster = new Cluster([
{ host: "127.0.0.1", port: 7000 }
], {
// Read from slave nodes when possible
scaleReads: "slave",
// Cluster retry strategy
clusterRetryStrategy: (times, reason) => {
console.log(`Cluster retry ${times}: ${reason?.message}`);
return times < 10 ? Math.min(times * 100, 5000) : null;
},
// Max redirections before giving up
maxRedirections: 16,
// Enable autopipelining for performance
enableAutoPipelining: true,
// Custom Redis options for each node
redisOptions: {
password: "cluster-password",
connectTimeout: 5000,
commandTimeout: 2000
},
// NAT mapping for Docker/cloud environments
natMap: {
"172.17.0.2:7000": { host: "redis-node-1.example.com", port: 7000 },
"172.17.0.3:7001": { host: "redis-node-2.example.com", port: 7001 }
}
});The Cluster client emits events for connection lifecycle and cluster state changes.
// Connection events (inherited from EventEmitter)
on(event: 'connect', listener: (address?: string) => void): this;
on(event: 'ready', listener: () => void): this;
on(event: 'error', listener: (err: Error) => void): this;
on(event: 'close', listener: () => void): this;
on(event: 'reconnecting', listener: (ms: number) => void): this;
on(event: 'end', listener: () => void): this;
// Cluster-specific events
on(event: 'node error', listener: (err: Error, address: string) => void): this;
on(event: '+node', listener: (address: string) => void): this;
on(event: '-node', listener: (address: string) => void): this;Usage Examples:
const cluster = new Cluster([{ host: "127.0.0.1", port: 7000 }]);
cluster.on('ready', () => {
console.log('Cluster is ready');
});
cluster.on('error', (err) => {
console.error('Cluster error:', err);
});
cluster.on('node error', (err, address) => {
console.error(`Node ${address} error:`, err);
});
cluster.on('+node', (address) => {
console.log(`Node added: ${address}`);
});
cluster.on('-node', (address) => {
console.log(`Node removed: ${address}`);
});Implement custom logic for directing read commands to specific nodes.
type ScaleReadsFunction = (node: {
role: string;
key: string;
options: any;
}, command: {
name: string;
args: any[];
}) => Redis | null;const cluster = new Cluster(nodes, {
scaleReads: (node, command) => {
// Direct specific commands to masters only
if (['eval', 'evalsha'].includes(command.name.toLowerCase())) {
return node.role === 'master' ? node : null;
}
// Use slaves for read operations
return node.role === 'slave' ? node : null;
}
});Create new cluster instances with independent connections but shared configuration.
duplicate(
overrideStartupNodes?: ClusterNode[],
overrideOptions?: ClusterOptions
): Cluster;const originalCluster = new Cluster([...nodes], options);
// Duplicate with same configuration
const dup1 = originalCluster.duplicate();
// Duplicate with different nodes
const dup2 = originalCluster.duplicate([
{ host: "new-cluster-1.example.com", port: 7000 }
]);
// Duplicate with different options
const dup3 = originalCluster.duplicate(undefined, {
scaleReads: "master",
maxRedirections: 8
});type Callback<T> = (err?: Error | null, result?: T) => void;
interface DNSLookupFunction {
(hostname: string, options: any, callback: Function): void;
}