# Cluster Operations

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.

## Capabilities

### Cluster Class

Main client class for Redis Cluster deployments with automatic slot mapping and node management.

```typescript { .api }
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:**

```typescript
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");
```

### Node Management

Access and manage individual Redis nodes within the cluster.

```typescript { .api }
// Get nodes by role
nodes(role?: NodeRole): Redis[];

// Refresh slot mapping from cluster
refreshSlotsCache(callback?: Callback<void>): void;
```

**Usage Examples:**

```typescript
// 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");
});
```

### Cluster Configuration

Comprehensive configuration options extending Redis options with cluster-specific settings.

```typescript { .api }
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:**

```typescript
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 }
  }
});
```

### Cluster Events

The Cluster client emits events for connection lifecycle and cluster state changes.

```typescript { .api }
// 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:**

```typescript
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}`);
});
```

## Advanced Features

### Custom Scale Reads Function

Implement custom logic for directing read commands to specific nodes.

```typescript { .api }
type ScaleReadsFunction = (node: {
  role: string;
  key: string;
  options: any;
}, command: {
  name: string;
  args: any[];
}) => Redis | null;
```

```typescript
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;
  }
});
```

### Cluster Connection Duplication

Create new cluster instances with independent connections but shared configuration.

```typescript { .api }
duplicate(
  overrideStartupNodes?: ClusterNode[], 
  overrideOptions?: ClusterOptions
): Cluster;
```

```typescript
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
});
```

## Types

```typescript { .api }
type Callback<T> = (err?: Error | null, result?: T) => void;

interface DNSLookupFunction {
  (hostname: string, options: any, callback: Function): void;
}
```