Horizontal Scaling
By default PP uses an in-memory adapter. For multi-instance deployments, swap in @painda/redis — a binary-native Redis adapter that is significantly faster than Socket.io's Redis adapter (which uses JSON for inter-node pub/sub).
Quick Setup
npm install @painda/redisimport { PPServer } from "@painda/core";
import { RedisAdapter } from "@painda/redis";
const server = new PPServer({
port: 3000,
adapter: new RedisAdapter({
host: "localhost",
port: 6379,
}),
});Maximum Compression with Schema Registry
Combine RedisAdapter with a schema registry to encode event types as 2-byte IDs instead of full strings — dramatically reducing Redis bandwidth at scale.
import { PPServer, PPSchemaRegistry, structSerializer } from "@painda/core";
import { RedisAdapter } from "@painda/redis";
const registry = new PPSchemaRegistry();
registry.register("player:move", structSerializer(1, [
{ name: "x", type: "float32" },
{ name: "y", type: "float32" },
]));
// Both server and adapter share the same registry
const server = new PPServer({
port: 3000,
registry,
adapter: new RedisAdapter({ host: "redis", registry }),
});
// "player:move" encoded as 2-byte ID in Redis pub/sub
// vs. Socket.io: JSON.stringify({ type: "player:move", ... })Binary Wire Format
Socket.io's Redis adapter uses JSON.stringify for every inter-node message. PP uses a custom compact binary format:
| Bytes | Field |
|---|---|
| uint8 | excludeLen — length of excludeClientId (0 = no exclude) |
| N bytes | excludeId — utf8 client ID to exclude |
| uint16 | typeId — 0 = string type, >0 = schema registry ID |
| uint16 + utf8 | type string (only when typeId = 0) |
| rest | payload — JSON bytes (no registry) or schema-binary (with registry) |
Adapter Interface
interface PPAdapter {
publish(channel, message, excludeClientId?): Promise<void>;
subscribe(channel, callback): Promise<void>;
unsubscribe(channel): Promise<void>;
addToRoom(room, clientId): Promise<void>;
removeFromRoom(room, clientId): Promise<void>;
getClientsInRoom(room): Promise<Set<string>>;
getClientRooms(clientId): Promise<Set<string>>;
close(): Promise<void>;
}Implement this interface for any pub/sub backend (NATS, Postgres, etc.). When using an adapter, server.to(room).emit() and server.broadcast() automatically fan out to all instances in the cluster.
See full reference: @painda/redis · @painda/core