@hocuspocus/extension-redis 3.2.4 → 3.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hocuspocus-redis.cjs +36 -22
- package/dist/hocuspocus-redis.cjs.map +1 -1
- package/dist/hocuspocus-redis.esm.js +36 -22
- package/dist/hocuspocus-redis.esm.js.map +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +2 -2
- package/dist/packages/extension-webhook/src/index.d.ts +2 -2
- package/dist/packages/server/src/Document.d.ts +2 -0
- package/package.json +3 -4
- package/src/Redis.ts +62 -35
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var crypto = require('node:crypto');
|
|
3
4
|
var server = require('@hocuspocus/server');
|
|
4
5
|
var redlock = require('@sesamecare-oss/redlock');
|
|
5
6
|
var RedisClient = require('ioredis');
|
|
6
|
-
var uuid = require('uuid');
|
|
7
7
|
|
|
8
8
|
class Redis {
|
|
9
9
|
constructor(configuration) {
|
|
@@ -17,7 +17,7 @@ class Redis {
|
|
|
17
17
|
port: 6379,
|
|
18
18
|
host: "127.0.0.1",
|
|
19
19
|
prefix: "hocuspocus",
|
|
20
|
-
identifier: `host-${
|
|
20
|
+
identifier: `host-${crypto.randomUUID()}`,
|
|
21
21
|
lockTimeout: 1000,
|
|
22
22
|
disconnectDelay: 1000,
|
|
23
23
|
};
|
|
@@ -105,7 +105,7 @@ class Redis {
|
|
|
105
105
|
}
|
|
106
106
|
this.sub.on("messageBuffer", this.handleIncomingMessage);
|
|
107
107
|
this.redlock = new redlock.Redlock([this.pub], {
|
|
108
|
-
|
|
108
|
+
retryCount: 0,
|
|
109
109
|
});
|
|
110
110
|
const identifierBuffer = Buffer.from(this.configuration.identifier, "utf-8");
|
|
111
111
|
this.messagePrefix = Buffer.concat([
|
|
@@ -179,32 +179,46 @@ class Redis {
|
|
|
179
179
|
// to avoid conflict with other instances storing the same document.
|
|
180
180
|
const resource = this.lockKey(documentName);
|
|
181
181
|
const ttl = this.configuration.lockTimeout;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
try {
|
|
183
|
+
await this.redlock.acquire([resource], ttl);
|
|
184
|
+
const oldLock = this.locks.get(resource);
|
|
185
|
+
if (oldLock) {
|
|
186
|
+
await oldLock.release;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
//based on: https://github.com/sesamecare/redlock/blob/508e00dcd1e4d2bc6373ce455f4fe847e98a9aab/src/index.ts#L347-L349
|
|
191
|
+
if (error ==
|
|
192
|
+
"ExecutionError: The operation was unable to achieve a quorum during its retry window.") {
|
|
193
|
+
// Expected behavior: Could not acquire lock, another instance locked it already.
|
|
194
|
+
// No further `onStoreDocument` hooks will be executed; should throw a silent error with no message.
|
|
195
|
+
throw new Error("", {
|
|
196
|
+
cause: "Could not acquire lock, another instance locked it already.",
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
//unexpected error
|
|
200
|
+
console.error("unexpected error:", error);
|
|
201
|
+
throw error;
|
|
186
202
|
}
|
|
187
|
-
this.locks.set(resource, { lock });
|
|
188
203
|
}
|
|
189
204
|
/**
|
|
190
205
|
* Release the Redis lock, so other instances can store documents.
|
|
191
206
|
*/
|
|
192
|
-
async afterStoreDocument({ documentName, socketId }) {
|
|
207
|
+
async afterStoreDocument({ documentName, socketId, }) {
|
|
193
208
|
const lockKey = this.lockKey(documentName);
|
|
194
209
|
const lock = this.locks.get(lockKey);
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.locks.delete(lockKey);
|
|
210
|
+
if (lock) {
|
|
211
|
+
try {
|
|
212
|
+
// Always try to unlock and clean up the lock
|
|
213
|
+
lock.release = lock.lock.release();
|
|
214
|
+
await lock.release;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Lock will expire on its own after timeout
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
this.locks.delete(lockKey);
|
|
221
|
+
}
|
|
208
222
|
}
|
|
209
223
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
210
224
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-redis.cjs","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["
|
|
1
|
+
{"version":3,"file":"hocuspocus-redis.cjs","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["IncomingMessage","MessageReceiver","Redlock","OutgoingMessage"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import { IncomingMessage, MessageReceiver, OutgoingMessage } from '@hocuspocus/server';
|
|
2
3
|
import { Redlock } from '@sesamecare-oss/redlock';
|
|
3
4
|
import RedisClient from 'ioredis';
|
|
4
|
-
import { v4 } from 'uuid';
|
|
5
5
|
|
|
6
6
|
class Redis {
|
|
7
7
|
constructor(configuration) {
|
|
@@ -15,7 +15,7 @@ class Redis {
|
|
|
15
15
|
port: 6379,
|
|
16
16
|
host: "127.0.0.1",
|
|
17
17
|
prefix: "hocuspocus",
|
|
18
|
-
identifier: `host-${
|
|
18
|
+
identifier: `host-${crypto.randomUUID()}`,
|
|
19
19
|
lockTimeout: 1000,
|
|
20
20
|
disconnectDelay: 1000,
|
|
21
21
|
};
|
|
@@ -103,7 +103,7 @@ class Redis {
|
|
|
103
103
|
}
|
|
104
104
|
this.sub.on("messageBuffer", this.handleIncomingMessage);
|
|
105
105
|
this.redlock = new Redlock([this.pub], {
|
|
106
|
-
|
|
106
|
+
retryCount: 0,
|
|
107
107
|
});
|
|
108
108
|
const identifierBuffer = Buffer.from(this.configuration.identifier, "utf-8");
|
|
109
109
|
this.messagePrefix = Buffer.concat([
|
|
@@ -177,32 +177,46 @@ class Redis {
|
|
|
177
177
|
// to avoid conflict with other instances storing the same document.
|
|
178
178
|
const resource = this.lockKey(documentName);
|
|
179
179
|
const ttl = this.configuration.lockTimeout;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
try {
|
|
181
|
+
await this.redlock.acquire([resource], ttl);
|
|
182
|
+
const oldLock = this.locks.get(resource);
|
|
183
|
+
if (oldLock) {
|
|
184
|
+
await oldLock.release;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
//based on: https://github.com/sesamecare/redlock/blob/508e00dcd1e4d2bc6373ce455f4fe847e98a9aab/src/index.ts#L347-L349
|
|
189
|
+
if (error ==
|
|
190
|
+
"ExecutionError: The operation was unable to achieve a quorum during its retry window.") {
|
|
191
|
+
// Expected behavior: Could not acquire lock, another instance locked it already.
|
|
192
|
+
// No further `onStoreDocument` hooks will be executed; should throw a silent error with no message.
|
|
193
|
+
throw new Error("", {
|
|
194
|
+
cause: "Could not acquire lock, another instance locked it already.",
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
//unexpected error
|
|
198
|
+
console.error("unexpected error:", error);
|
|
199
|
+
throw error;
|
|
184
200
|
}
|
|
185
|
-
this.locks.set(resource, { lock });
|
|
186
201
|
}
|
|
187
202
|
/**
|
|
188
203
|
* Release the Redis lock, so other instances can store documents.
|
|
189
204
|
*/
|
|
190
|
-
async afterStoreDocument({ documentName, socketId }) {
|
|
205
|
+
async afterStoreDocument({ documentName, socketId, }) {
|
|
191
206
|
const lockKey = this.lockKey(documentName);
|
|
192
207
|
const lock = this.locks.get(lockKey);
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.locks.delete(lockKey);
|
|
208
|
+
if (lock) {
|
|
209
|
+
try {
|
|
210
|
+
// Always try to unlock and clean up the lock
|
|
211
|
+
lock.release = lock.lock.release();
|
|
212
|
+
await lock.release;
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Lock will expire on its own after timeout
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
this.locks.delete(lockKey);
|
|
219
|
+
}
|
|
206
220
|
}
|
|
207
221
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
208
222
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-redis.esm.js","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":[
|
|
1
|
+
{"version":3,"file":"hocuspocus-redis.esm.js","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Extension, Hocuspocus, afterLoadDocumentPayload, afterStoreDocumentPayload, beforeBroadcastStatelessPayload, onAwarenessUpdatePayload, onChangePayload, onConfigurePayload, onDisconnectPayload, onStoreDocumentPayload } from "@hocuspocus/server";
|
|
2
|
-
import {
|
|
2
|
+
import { type ExecutionResult, type Lock, Redlock } from "@sesamecare-oss/redlock";
|
|
3
3
|
import type { Cluster, ClusterNode, ClusterOptions, RedisOptions } from "ioredis";
|
|
4
4
|
import RedisClient from "ioredis";
|
|
5
5
|
export type RedisInstance = RedisClient | Cluster;
|
|
@@ -101,7 +101,7 @@ export declare class Redis implements Extension {
|
|
|
101
101
|
/**
|
|
102
102
|
* Release the Redis lock, so other instances can store documents.
|
|
103
103
|
*/
|
|
104
|
-
afterStoreDocument({ documentName, socketId }: afterStoreDocumentPayload): Promise<void>;
|
|
104
|
+
afterStoreDocument({ documentName, socketId, }: afterStoreDocumentPayload): Promise<void>;
|
|
105
105
|
/**
|
|
106
106
|
* Handle awareness update messages received directly by this Hocuspocus instance.
|
|
107
107
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Extension, onChangePayload, onConnectPayload,
|
|
2
|
-
import type { Doc } from "yjs";
|
|
1
|
+
import type { Extension, onChangePayload, onConnectPayload, onDisconnectPayload, onLoadDocumentPayload } from "@hocuspocus/server";
|
|
3
2
|
import type { Transformer } from "@hocuspocus/transformer";
|
|
3
|
+
import type { Doc } from "yjs";
|
|
4
4
|
export declare enum Events {
|
|
5
5
|
onChange = "change",
|
|
6
6
|
onConnect = "connect",
|
|
@@ -2,6 +2,7 @@ import type WebSocket from "ws";
|
|
|
2
2
|
import { Awareness } from "y-protocols/awareness";
|
|
3
3
|
import { Doc } from "yjs";
|
|
4
4
|
import type Connection from "./Connection.ts";
|
|
5
|
+
import { Mutex } from "async-mutex";
|
|
5
6
|
export declare class Document extends Doc {
|
|
6
7
|
awareness: Awareness;
|
|
7
8
|
callbacks: {
|
|
@@ -16,6 +17,7 @@ export declare class Document extends Doc {
|
|
|
16
17
|
name: string;
|
|
17
18
|
isLoading: boolean;
|
|
18
19
|
isDestroyed: boolean;
|
|
20
|
+
saveMutex: Mutex;
|
|
19
21
|
/**
|
|
20
22
|
* Constructor.
|
|
21
23
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/extension-redis",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "Scale Hocuspocus horizontally with Redis",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -31,12 +31,11 @@
|
|
|
31
31
|
"@types/lodash.debounce": "^4.0.6"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@hocuspocus/server": "^3.2.
|
|
34
|
+
"@hocuspocus/server": "^3.2.5",
|
|
35
35
|
"@sesamecare-oss/redlock": "^1.4.0",
|
|
36
36
|
"ioredis": "^5.6.1",
|
|
37
37
|
"kleur": "^4.1.4",
|
|
38
|
-
"lodash.debounce": "^4.0.8"
|
|
39
|
-
"uuid": "^13.0.0"
|
|
38
|
+
"lodash.debounce": "^4.0.8"
|
|
40
39
|
},
|
|
41
40
|
"peerDependencies": {
|
|
42
41
|
"y-protocols": "^1.0.6",
|
package/src/Redis.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import type {
|
|
2
3
|
Document,
|
|
3
4
|
Extension,
|
|
@@ -16,11 +17,19 @@ import {
|
|
|
16
17
|
MessageReceiver,
|
|
17
18
|
OutgoingMessage,
|
|
18
19
|
} from "@hocuspocus/server";
|
|
19
|
-
import {
|
|
20
|
-
|
|
20
|
+
import {
|
|
21
|
+
type ExecutionResult,
|
|
22
|
+
type Lock,
|
|
23
|
+
Redlock,
|
|
24
|
+
} from "@sesamecare-oss/redlock";
|
|
25
|
+
import type {
|
|
26
|
+
Cluster,
|
|
27
|
+
ClusterNode,
|
|
28
|
+
ClusterOptions,
|
|
29
|
+
RedisOptions,
|
|
30
|
+
} from "ioredis";
|
|
21
31
|
import RedisClient from "ioredis";
|
|
22
|
-
|
|
23
|
-
export type RedisInstance = RedisClient | Cluster
|
|
32
|
+
export type RedisInstance = RedisClient | Cluster;
|
|
24
33
|
export interface Configuration {
|
|
25
34
|
/**
|
|
26
35
|
* Redis port
|
|
@@ -80,7 +89,7 @@ export class Redis implements Extension {
|
|
|
80
89
|
port: 6379,
|
|
81
90
|
host: "127.0.0.1",
|
|
82
91
|
prefix: "hocuspocus",
|
|
83
|
-
identifier: `host-${
|
|
92
|
+
identifier: `host-${crypto.randomUUID()}`,
|
|
84
93
|
lockTimeout: 1000,
|
|
85
94
|
disconnectDelay: 1000,
|
|
86
95
|
};
|
|
@@ -95,7 +104,7 @@ export class Redis implements Extension {
|
|
|
95
104
|
|
|
96
105
|
redlock: Redlock;
|
|
97
106
|
|
|
98
|
-
locks = new Map<string, {lock: Lock; release?: Promise<ExecutionResult>}>();
|
|
107
|
+
locks = new Map<string, { lock: Lock; release?: Promise<ExecutionResult> }>();
|
|
99
108
|
|
|
100
109
|
messagePrefix: Buffer;
|
|
101
110
|
|
|
@@ -136,7 +145,7 @@ export class Redis implements Extension {
|
|
|
136
145
|
this.sub.on("messageBuffer", this.handleIncomingMessage);
|
|
137
146
|
|
|
138
147
|
this.redlock = new Redlock([this.pub], {
|
|
139
|
-
|
|
148
|
+
retryCount: 0,
|
|
140
149
|
});
|
|
141
150
|
|
|
142
151
|
const identifierBuffer = Buffer.from(
|
|
@@ -236,37 +245,55 @@ export class Redis implements Extension {
|
|
|
236
245
|
* Before the document is stored, make sure to set a lock in Redis.
|
|
237
246
|
* That’s meant to avoid conflicts with other instances trying to store the document.
|
|
238
247
|
*/
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
248
|
+
async onStoreDocument({ documentName }: onStoreDocumentPayload) {
|
|
249
|
+
// Attempt to acquire a lock and read lastReceivedTimestamp from Redis,
|
|
250
|
+
// to avoid conflict with other instances storing the same document.
|
|
251
|
+
const resource = this.lockKey(documentName);
|
|
252
|
+
const ttl = this.configuration.lockTimeout;
|
|
253
|
+
try {
|
|
254
|
+
await this.redlock.acquire([resource], ttl);
|
|
255
|
+
const oldLock = this.locks.get(resource);
|
|
256
|
+
if (oldLock) {
|
|
257
|
+
await oldLock.release;
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
//based on: https://github.com/sesamecare/redlock/blob/508e00dcd1e4d2bc6373ce455f4fe847e98a9aab/src/index.ts#L347-L349
|
|
261
|
+
if (
|
|
262
|
+
error ==
|
|
263
|
+
"ExecutionError: The operation was unable to achieve a quorum during its retry window."
|
|
264
|
+
) {
|
|
265
|
+
// Expected behavior: Could not acquire lock, another instance locked it already.
|
|
266
|
+
// No further `onStoreDocument` hooks will be executed; should throw a silent error with no message.
|
|
267
|
+
throw new Error("", {
|
|
268
|
+
cause: "Could not acquire lock, another instance locked it already.",
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//unexpected error
|
|
272
|
+
console.error("unexpected error:", error);
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
251
276
|
|
|
252
277
|
/**
|
|
253
278
|
* Release the Redis lock, so other instances can store documents.
|
|
254
279
|
*/
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
280
|
+
async afterStoreDocument({
|
|
281
|
+
documentName,
|
|
282
|
+
socketId,
|
|
283
|
+
}: afterStoreDocumentPayload) {
|
|
284
|
+
const lockKey = this.lockKey(documentName);
|
|
285
|
+
const lock = this.locks.get(lockKey);
|
|
286
|
+
if (lock) {
|
|
287
|
+
try {
|
|
288
|
+
// Always try to unlock and clean up the lock
|
|
289
|
+
lock.release = lock.lock.release();
|
|
290
|
+
await lock.release;
|
|
291
|
+
} catch {
|
|
292
|
+
// Lock will expire on its own after timeout
|
|
293
|
+
} finally {
|
|
294
|
+
this.locks.delete(lockKey);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
270
297
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
271
298
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
272
299
|
if (socketId === "server") {
|
|
@@ -390,7 +417,7 @@ export class Redis implements Extension {
|
|
|
390
417
|
}
|
|
391
418
|
});
|
|
392
419
|
|
|
393
|
-
if(document) {
|
|
420
|
+
if (document) {
|
|
394
421
|
this.instance.unloadDocument(document);
|
|
395
422
|
}
|
|
396
423
|
};
|