CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-socket-io--redis-adapter

Redis adapter for Socket.IO that enables broadcasting packets between multiple Socket.IO servers through Redis pub/sub functionality

Pending
Overview
Eval results
Files

sharded-adapter.mddocs/

Sharded Redis Adapter

Advanced Redis adapter implementation utilizing Redis 7.0+ sharded pub/sub features for improved scalability and performance. Sharded pub/sub helps scale Socket.IO applications by distributing channel load across Redis cluster nodes.

Capabilities

Create Sharded Adapter

Creates a sharded Redis adapter factory function optimized for Redis 7.0+ sharded pub/sub.

/**
 * Creates a sharded Redis adapter factory using Redis 7.0+ sharded pub/sub
 * @param pubClient - Redis client used to publish (from the 'redis' package)
 * @param subClient - Redis client used to subscribe (from the 'redis' package)
 * @param opts - Optional configuration options
 * @returns Function that creates ShardedRedisAdapter instances for namespaces
 */
function createShardedAdapter(
  pubClient: any,
  subClient: any,
  opts?: ShardedRedisAdapterOptions
): (nsp: any) => ShardedRedisAdapter;

Usage Examples:

import { Server } from "socket.io";
import { createClient } from "redis";
import { createShardedAdapter } from "@socket.io/redis-adapter";

// Basic sharded adapter setup
const pubClient = createClient({ host: "localhost", port: 6379 });
const subClient = pubClient.duplicate();

await Promise.all([
  pubClient.connect(),
  subClient.connect()
]);

const io = new Server({
  adapter: createShardedAdapter(pubClient, subClient)
});

io.listen(3000);

// With custom options
const io2 = new Server({
  adapter: createShardedAdapter(pubClient, subClient, {
    channelPrefix: "my-app",
    subscriptionMode: "dynamic-private"
  })
});

ShardedRedisAdapter Class

Main sharded adapter implementation that extends ClusterAdapter with Redis sharded pub/sub functionality.

/**
 * Sharded Redis adapter class using Redis 7.0+ sharded pub/sub features
 * Extends ClusterAdapter from socket.io-adapter
 */
class ShardedRedisAdapter extends ClusterAdapter {
  /**
   * Create a new ShardedRedisAdapter instance
   * @param nsp - Socket.IO namespace
   * @param pubClient - Redis client for publishing
   * @param subClient - Redis client for subscribing
   * @param opts - Configuration options
   */
  constructor(
    nsp: any,
    pubClient: any,
    subClient: any,
    opts?: ShardedRedisAdapterOptions
  );
}

Publishing Methods

Methods for publishing messages through sharded channels.

/**
 * Publishes a cluster message to appropriate sharded channel
 * @param message - Message to publish
 * @returns Promise resolving to offset (empty string for Redis)
 */
doPublish(message: ClusterMessage): Promise<Offset>;

/**
 * Publishes a response to the requesting server's specific channel
 * @param requesterUid - Unique ID of the requesting server
 * @param response - Response message to send
 * @returns Promise that resolves when message is published
 */
doPublishResponse(
  requesterUid: string,
  response: ClusterResponse
): Promise<void>;

Server Management

Methods for managing server connections and counts.

/**
 * Gets the count of servers using sharded pub/sub
 * Uses SHARDNUMSUB command for accurate server counting
 * @returns Promise resolving to number of connected servers
 */
serverCount(): Promise<number>;

Lifecycle Management

Methods for managing the sharded adapter lifecycle.

/**
 * Closes the sharded adapter and cleans up all subscriptions
 * Unsubscribes from base channels and dynamic room channels
 * @returns Promise that resolves when cleanup is complete
 */
close(): Promise<void>;

Configuration Options

interface ShardedRedisAdapterOptions {
  /**
   * The prefix for Redis Pub/Sub channels
   * @default "socket.io"
   */
  channelPrefix?: string;
  
  /**
   * Subscription mode that impacts the number of channels used:
   * 
   * - "static": 2 channels per namespace
   *   Useful with dynamic namespaces
   * 
   * - "dynamic": (2 + 1 per public room) channels per namespace
   *   Default value, optimal when some rooms have few clients
   *   Only creates channels for public rooms (not Socket IDs)
   * 
   * - "dynamic-private": Like "dynamic" but includes private rooms
   *   Creates separate channels for Socket ID rooms
   *   Useful for lots of 1:1 communication via socket.emit()
   * 
   * @default "dynamic"
   */
  subscriptionMode?: "static" | "dynamic" | "dynamic-private";
}

Subscription Modes Explained

Static Mode

  • Channels: 2 per namespace
  • Use Case: Dynamic namespaces with unpredictable room patterns
  • Pros: Predictable channel count, simple setup
  • Cons: All messages go through same channels, less optimization

Dynamic Mode (Default)

  • Channels: 2 base + 1 per public room
  • Use Case: Applications with some rooms having few clients
  • Pros: Optimized delivery, servers only get relevant room messages
  • Cons: More channels to manage
  • Note: Only public rooms get separate channels, not Socket ID rooms

Dynamic Private Mode

  • Channels: 2 base + 1 per room (including private/Socket ID rooms)
  • Use Case: Heavy 1:1 communication patterns
  • Pros: Maximum optimization for direct socket communication
  • Cons: Highest channel count, more subscription overhead

Usage Examples

Basic Sharded Setup

import { createClient } from "redis";
import { Server } from "socket.io";
import { createShardedAdapter } from "@socket.io/redis-adapter";

const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();

await Promise.all([
  pubClient.connect(),
  subClient.connect()
]);

const io = new Server({
  adapter: createShardedAdapter(pubClient, subClient)
});

// All Socket.IO operations work the same
io.emit("broadcast", "Hello everyone!");
io.to("room1").emit("room-message", "Hello room1!");

Advanced Configuration

const io = new Server({
  adapter: createShardedAdapter(pubClient, subClient, {
    channelPrefix: "my-game",
    subscriptionMode: "dynamic-private" // Optimize for 1:1 messaging
  })
});

// Efficient for direct socket communication
io.to(socketId).emit("private-message", "Hello specific user!");

Room-based Broadcasting

// With dynamic mode, each room gets its own optimized channel
io.to("lobby").emit("player-joined", { playerId: 123 });
io.to("game-room-1").emit("game-update", gameState);

// Servers not subscribed to these specific rooms won't receive these messages

Requirements and Limitations

Minimum Requirements

  • Redis: Version 7.0 or higher
  • redis package: Version 4.6.0 or higher
  • Node.js: Version 10.0.0 or higher

Limitations

  • ioredis with Redis Cluster: Not currently supported for sharded adapter
  • Redis Clients: Only the redis package is supported, not ioredis
  • Backward Compatibility: Cannot mix sharded and regular adapters in same deployment

Redis Commands Used

  • SPUBLISH: For publishing to sharded channels
  • SSUBSCRIBE/SUNSUBSCRIBE: For managing sharded subscriptions
  • SHARDNUMSUB: For counting subscribers to sharded channels

Migration from Regular Adapter

When migrating from the regular Redis adapter to the sharded adapter:

  1. Ensure Redis 7.0+: Verify your Redis version supports sharded pub/sub
  2. Update Redis Client: Use redis package v4.6.0+
  3. Change Import: Switch from createAdapter to createShardedAdapter
  4. Update Configuration: Review subscription mode for your use case
  5. Test Thoroughly: Sharded pub/sub has different delivery patterns
// Before (regular adapter)
import { createAdapter } from "@socket.io/redis-adapter";
const adapter = createAdapter(pubClient, subClient, {
  key: "my-app"
});

// After (sharded adapter)
import { createShardedAdapter } from "@socket.io/redis-adapter";
const adapter = createShardedAdapter(pubClient, subClient, {
  channelPrefix: "my-app",
  subscriptionMode: "dynamic"
});

Install with Tessl CLI

npx tessl i tessl/npm-socket-io--redis-adapter

docs

index.md

regular-adapter.md

sharded-adapter.md

utilities.md

tile.json