Redis
Hocuspocus can be scaled horizontally using the Redis extension. You can spawn multiple instances of the server behind a load balancer and sync changes and awareness states through Redis. Hocuspocus will propagate all received updates to all other instances using Redis and thus forward updates to all clients of all Hocuspocus instances.
The Redis extension does not persist data; it only syncs data between instances. Use the Database extension to store your documents.
Please note that all messages will be handled on all instances of Hocuspocus, so if you are trying to reduce cpu load by spawning multiple servers, you should not connect them via Redis.
Thanks to @tommoor for writing the initial implementation of that extension.
Installation
Install the Redis extension with:
npm install @hocuspocus/extension-redisConfiguration
For a full documentation on all available Redis and Redis cluster options, check out the ioredis API docs.
import { Server } from "@hocuspocus/server";
import { Redis } from "@hocuspocus/extension-redis";
const server = new Server({
extensions: [
new Redis({
// [required] Hostname of your Redis instance
host: "127.0.0.1",
// [required] Port of your Redis instance
port: 6379,
// [optional] Maximum time in milliseconds to wait for the current state
// from another instance when loading a document. Defaults to 1000.
awaitInitialSyncTimeout: 1000,
}),
],
});
server.listen();Consistent state on load
When an instance loads a document that is already open on another instance, the Redis extension waits for that instance to send its current state. This ensures that newly connected clients and direct connections receive changes that may not have been persisted yet. If no other instance has the document open, loading is not delayed.
The awaitInitialSyncTimeout option limits how long the extension waits, in milliseconds. It defaults to 1000. Set it to 0 to disable waiting and use fire-and-forget synchronization instead.
Usage
The Redis extension works well with the database extension. Once an instance stores a document, it’s blocked for all other instances to avoid write conflicts.
import { Server } from "@hocuspocus/server";
import { Logger } from "@hocuspocus/extension-logger";
import { Redis } from "@hocuspocus/extension-redis";
import { SQLite } from "@hocuspocus/extension-sqlite";
// Server 1
const server = new Server({
name: "server-1", // make sure to use unique server names
port: 1234,
extensions: [
new Logger(),
new Redis({
host: "127.0.0.1", // make sure to use the same Redis instance :-)
port: 6379,
}),
new SQLite(),
],
});
server.listen();
// Server 2
const anotherServer = new Server({
name: "server-2",
port: 1235,
extensions: [
new Logger(),
new Redis({
host: "127.0.0.1",
port: 6379,
}),
new SQLite(),
],
});
anotherServer.listen();