@hocuspocus/extension-redis 3.2.1 → 3.2.2
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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var server = require('@hocuspocus/server');
|
|
4
|
+
var redlock = require('@sesamecare-oss/redlock');
|
|
4
5
|
var RedisClient = require('ioredis');
|
|
5
|
-
var Redlock = require('redlock');
|
|
6
6
|
var uuid = require('uuid');
|
|
7
7
|
|
|
8
8
|
class Redis {
|
|
@@ -47,7 +47,7 @@ class Redis {
|
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
new server.MessageReceiver(message, this.redisTransactionOrigin).apply(document, undefined, (reply) => {
|
|
50
|
-
return this.pub.
|
|
50
|
+
return this.pub.publish(this.pubKey(document.name), this.encodeMessage(reply));
|
|
51
51
|
});
|
|
52
52
|
};
|
|
53
53
|
/**
|
|
@@ -104,8 +104,8 @@ class Redis {
|
|
|
104
104
|
this.sub = new RedisClient(port, host, options !== null && options !== void 0 ? options : {});
|
|
105
105
|
}
|
|
106
106
|
this.sub.on("messageBuffer", this.handleIncomingMessage);
|
|
107
|
-
this.redlock = new Redlock([this.pub], {
|
|
108
|
-
|
|
107
|
+
this.redlock = new redlock.Redlock([this.pub], {
|
|
108
|
+
driftFactor: 0.1
|
|
109
109
|
});
|
|
110
110
|
const identifierBuffer = Buffer.from(this.configuration.identifier, "utf-8");
|
|
111
111
|
this.messagePrefix = Buffer.concat([
|
|
@@ -161,14 +161,14 @@ class Redis {
|
|
|
161
161
|
const syncMessage = new server.OutgoingMessage(documentName)
|
|
162
162
|
.createSyncMessage()
|
|
163
163
|
.writeFirstSyncStepFor(document);
|
|
164
|
-
return this.pub.
|
|
164
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(syncMessage.toUint8Array()));
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* Let’s ask Redis who is connected already.
|
|
168
168
|
*/
|
|
169
169
|
async requestAwarenessFromOtherInstances(documentName) {
|
|
170
170
|
const awarenessMessage = new server.OutgoingMessage(documentName).writeQueryAwareness();
|
|
171
|
-
return this.pub.
|
|
171
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(awarenessMessage.toUint8Array()));
|
|
172
172
|
}
|
|
173
173
|
/**
|
|
174
174
|
* Before the document is stored, make sure to set a lock in Redis.
|
|
@@ -177,32 +177,35 @@ class Redis {
|
|
|
177
177
|
async onStoreDocument({ documentName }) {
|
|
178
178
|
// Attempt to acquire a lock and read lastReceivedTimestamp from Redis,
|
|
179
179
|
// to avoid conflict with other instances storing the same document.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
this.locks.set(this.lockKey(documentName), lock);
|
|
190
|
-
resolve(undefined);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
180
|
+
const resource = this.lockKey(documentName);
|
|
181
|
+
const ttl = this.configuration.lockTimeout;
|
|
182
|
+
const lock = await this.redlock.acquire([resource], ttl);
|
|
183
|
+
const oldLock = this.locks.get(resource);
|
|
184
|
+
if (oldLock) {
|
|
185
|
+
await oldLock.release;
|
|
186
|
+
}
|
|
187
|
+
this.locks.set(resource, { lock });
|
|
193
188
|
}
|
|
194
189
|
/**
|
|
195
190
|
* Release the Redis lock, so other instances can store documents.
|
|
196
191
|
*/
|
|
197
|
-
async afterStoreDocument({ documentName, socketId
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
async afterStoreDocument({ documentName, socketId }) {
|
|
193
|
+
const lockKey = this.lockKey(documentName);
|
|
194
|
+
const lock = this.locks.get(lockKey);
|
|
195
|
+
if (!lock) {
|
|
196
|
+
throw new Error(`Lock created in onStoreDocument not found in afterStoreDocument: ${lockKey}`);
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
// Always try to unlock and clean up the lock
|
|
200
|
+
lock.release = lock.lock.release();
|
|
201
|
+
await lock.release;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Lock will expire on its own after timeout
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
this.locks.delete(lockKey);
|
|
208
|
+
}
|
|
206
209
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
207
210
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
208
211
|
if (socketId === "server") {
|
|
@@ -233,7 +236,7 @@ class Redis {
|
|
|
233
236
|
async onAwarenessUpdate({ documentName, awareness, added, updated, removed, }) {
|
|
234
237
|
const changedClients = added.concat(updated, removed);
|
|
235
238
|
const message = new server.OutgoingMessage(documentName).createAwarenessUpdateMessage(awareness, changedClients);
|
|
236
|
-
return this.pub.
|
|
239
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(message.toUint8Array()));
|
|
237
240
|
}
|
|
238
241
|
/**
|
|
239
242
|
* if the ydoc changed, we'll need to inform other Hocuspocus servers about it.
|
|
@@ -245,7 +248,7 @@ class Redis {
|
|
|
245
248
|
}
|
|
246
249
|
async beforeBroadcastStateless(data) {
|
|
247
250
|
const message = new server.OutgoingMessage(data.documentName).writeBroadcastStateless(data.payload);
|
|
248
|
-
return this.pub.
|
|
251
|
+
return this.pub.publish(this.pubKey(data.documentName), this.encodeMessage(message.toUint8Array()));
|
|
249
252
|
}
|
|
250
253
|
/**
|
|
251
254
|
* Kill the Redlock connection immediately.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-redis.cjs","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["uuid","IncomingMessage","MessageReceiver","OutgoingMessage"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"hocuspocus-redis.cjs","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["uuid","IncomingMessage","MessageReceiver","Redlock","OutgoingMessage"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IncomingMessage, MessageReceiver, OutgoingMessage } from '@hocuspocus/server';
|
|
2
|
+
import { Redlock } from '@sesamecare-oss/redlock';
|
|
2
3
|
import RedisClient from 'ioredis';
|
|
3
|
-
import Redlock from 'redlock';
|
|
4
4
|
import { v4 } from 'uuid';
|
|
5
5
|
|
|
6
6
|
class Redis {
|
|
@@ -45,7 +45,7 @@ class Redis {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
new MessageReceiver(message, this.redisTransactionOrigin).apply(document, undefined, (reply) => {
|
|
48
|
-
return this.pub.
|
|
48
|
+
return this.pub.publish(this.pubKey(document.name), this.encodeMessage(reply));
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
@@ -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
|
+
driftFactor: 0.1
|
|
107
107
|
});
|
|
108
108
|
const identifierBuffer = Buffer.from(this.configuration.identifier, "utf-8");
|
|
109
109
|
this.messagePrefix = Buffer.concat([
|
|
@@ -159,14 +159,14 @@ class Redis {
|
|
|
159
159
|
const syncMessage = new OutgoingMessage(documentName)
|
|
160
160
|
.createSyncMessage()
|
|
161
161
|
.writeFirstSyncStepFor(document);
|
|
162
|
-
return this.pub.
|
|
162
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(syncMessage.toUint8Array()));
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
165
165
|
* Let’s ask Redis who is connected already.
|
|
166
166
|
*/
|
|
167
167
|
async requestAwarenessFromOtherInstances(documentName) {
|
|
168
168
|
const awarenessMessage = new OutgoingMessage(documentName).writeQueryAwareness();
|
|
169
|
-
return this.pub.
|
|
169
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(awarenessMessage.toUint8Array()));
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
172
172
|
* Before the document is stored, make sure to set a lock in Redis.
|
|
@@ -175,32 +175,35 @@ class Redis {
|
|
|
175
175
|
async onStoreDocument({ documentName }) {
|
|
176
176
|
// Attempt to acquire a lock and read lastReceivedTimestamp from Redis,
|
|
177
177
|
// to avoid conflict with other instances storing the same document.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
this.locks.set(this.lockKey(documentName), lock);
|
|
188
|
-
resolve(undefined);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
178
|
+
const resource = this.lockKey(documentName);
|
|
179
|
+
const ttl = this.configuration.lockTimeout;
|
|
180
|
+
const lock = await this.redlock.acquire([resource], ttl);
|
|
181
|
+
const oldLock = this.locks.get(resource);
|
|
182
|
+
if (oldLock) {
|
|
183
|
+
await oldLock.release;
|
|
184
|
+
}
|
|
185
|
+
this.locks.set(resource, { lock });
|
|
191
186
|
}
|
|
192
187
|
/**
|
|
193
188
|
* Release the Redis lock, so other instances can store documents.
|
|
194
189
|
*/
|
|
195
|
-
async afterStoreDocument({ documentName, socketId
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
190
|
+
async afterStoreDocument({ documentName, socketId }) {
|
|
191
|
+
const lockKey = this.lockKey(documentName);
|
|
192
|
+
const lock = this.locks.get(lockKey);
|
|
193
|
+
if (!lock) {
|
|
194
|
+
throw new Error(`Lock created in onStoreDocument not found in afterStoreDocument: ${lockKey}`);
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
// Always try to unlock and clean up the lock
|
|
198
|
+
lock.release = lock.lock.release();
|
|
199
|
+
await lock.release;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Lock will expire on its own after timeout
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
this.locks.delete(lockKey);
|
|
206
|
+
}
|
|
204
207
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
205
208
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
206
209
|
if (socketId === "server") {
|
|
@@ -231,7 +234,7 @@ class Redis {
|
|
|
231
234
|
async onAwarenessUpdate({ documentName, awareness, added, updated, removed, }) {
|
|
232
235
|
const changedClients = added.concat(updated, removed);
|
|
233
236
|
const message = new OutgoingMessage(documentName).createAwarenessUpdateMessage(awareness, changedClients);
|
|
234
|
-
return this.pub.
|
|
237
|
+
return this.pub.publish(this.pubKey(documentName), this.encodeMessage(message.toUint8Array()));
|
|
235
238
|
}
|
|
236
239
|
/**
|
|
237
240
|
* if the ydoc changed, we'll need to inform other Hocuspocus servers about it.
|
|
@@ -243,7 +246,7 @@ class Redis {
|
|
|
243
246
|
}
|
|
244
247
|
async beforeBroadcastStateless(data) {
|
|
245
248
|
const message = new OutgoingMessage(data.documentName).writeBroadcastStateless(data.payload);
|
|
246
|
-
return this.pub.
|
|
249
|
+
return this.pub.publish(this.pubKey(data.documentName), this.encodeMessage(message.toUint8Array()));
|
|
247
250
|
}
|
|
248
251
|
/**
|
|
249
252
|
* Kill the Redlock connection immediately.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hocuspocus-redis.esm.js","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["uuid"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"hocuspocus-redis.esm.js","sources":["../src/Redis.ts"],"sourcesContent":[null],"names":["uuid"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Extension, Hocuspocus, afterLoadDocumentPayload, afterStoreDocumentPayload, beforeBroadcastStatelessPayload, onAwarenessUpdatePayload, onChangePayload, onConfigurePayload, onDisconnectPayload, onStoreDocumentPayload } from "@hocuspocus/server";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
2
|
+
import { Redlock, type ExecutionResult, type Lock } from '@sesamecare-oss/redlock';
|
|
3
|
+
import type { Cluster, ClusterNode, ClusterOptions, RedisOptions } from "ioredis";
|
|
4
|
+
import RedisClient from "ioredis";
|
|
5
|
+
export type RedisInstance = RedisClient | Cluster;
|
|
5
6
|
export interface Configuration {
|
|
6
7
|
/**
|
|
7
8
|
* Redis port
|
|
@@ -61,7 +62,10 @@ export declare class Redis implements Extension {
|
|
|
61
62
|
sub: RedisInstance;
|
|
62
63
|
instance: Hocuspocus;
|
|
63
64
|
redlock: Redlock;
|
|
64
|
-
locks: Map<string,
|
|
65
|
+
locks: Map<string, {
|
|
66
|
+
lock: Lock;
|
|
67
|
+
release?: Promise<ExecutionResult>;
|
|
68
|
+
}>;
|
|
65
69
|
messagePrefix: Buffer;
|
|
66
70
|
/**
|
|
67
71
|
* When we have a high frequency of updates to a document we don't need tons of setTimeouts
|
|
@@ -93,15 +97,15 @@ export declare class Redis implements Extension {
|
|
|
93
97
|
* Before the document is stored, make sure to set a lock in Redis.
|
|
94
98
|
* That’s meant to avoid conflicts with other instances trying to store the document.
|
|
95
99
|
*/
|
|
96
|
-
onStoreDocument({ documentName }: onStoreDocumentPayload): Promise<
|
|
100
|
+
onStoreDocument({ documentName }: onStoreDocumentPayload): Promise<void>;
|
|
97
101
|
/**
|
|
98
102
|
* Release the Redis lock, so other instances can store documents.
|
|
99
103
|
*/
|
|
100
|
-
afterStoreDocument({ documentName, socketId
|
|
104
|
+
afterStoreDocument({ documentName, socketId }: afterStoreDocumentPayload): Promise<void>;
|
|
101
105
|
/**
|
|
102
106
|
* Handle awareness update messages received directly by this Hocuspocus instance.
|
|
103
107
|
*/
|
|
104
|
-
onAwarenessUpdate({ documentName, awareness, added, updated, removed, }: onAwarenessUpdatePayload): Promise<
|
|
108
|
+
onAwarenessUpdate({ documentName, awareness, added, updated, removed, }: onAwarenessUpdatePayload): Promise<number>;
|
|
105
109
|
/**
|
|
106
110
|
* Handle incoming messages published on subscribed document channels.
|
|
107
111
|
* Note that this will also include messages from ourselves as it is not possible
|
|
@@ -117,7 +121,7 @@ export declare class Redis implements Extension {
|
|
|
117
121
|
* no one connected anymore.
|
|
118
122
|
*/
|
|
119
123
|
onDisconnect: ({ documentName }: onDisconnectPayload) => Promise<void>;
|
|
120
|
-
beforeBroadcastStateless(data: beforeBroadcastStatelessPayload): Promise<
|
|
124
|
+
beforeBroadcastStateless(data: beforeBroadcastStatelessPayload): Promise<number>;
|
|
121
125
|
/**
|
|
122
126
|
* Kill the Redlock connection immediately.
|
|
123
127
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hocuspocus/extension-redis",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "Scale Hocuspocus horizontally with Redis",
|
|
5
5
|
"homepage": "https://hocuspocus.dev",
|
|
6
6
|
"keywords": [
|
|
@@ -28,15 +28,14 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@types/lodash.debounce": "^4.0.6"
|
|
32
|
-
"@types/redlock": "^4.0.3"
|
|
31
|
+
"@types/lodash.debounce": "^4.0.6"
|
|
33
32
|
},
|
|
34
33
|
"dependencies": {
|
|
35
|
-
"@hocuspocus/server": "^3.2.
|
|
34
|
+
"@hocuspocus/server": "^3.2.2",
|
|
35
|
+
"@sesamecare-oss/redlock": "^1.4.0",
|
|
36
36
|
"ioredis": "^5.6.1",
|
|
37
37
|
"kleur": "^4.1.4",
|
|
38
38
|
"lodash.debounce": "^4.0.8",
|
|
39
|
-
"redlock": "^4.2.0",
|
|
40
39
|
"uuid": "^11.0.3"
|
|
41
40
|
},
|
|
42
41
|
"peerDependencies": {
|
package/src/Redis.ts
CHANGED
|
@@ -16,13 +16,11 @@ import {
|
|
|
16
16
|
MessageReceiver,
|
|
17
17
|
OutgoingMessage,
|
|
18
18
|
} from "@hocuspocus/server";
|
|
19
|
-
import
|
|
19
|
+
import {Redlock, type ExecutionResult, type Lock} from '@sesamecare-oss/redlock';
|
|
20
|
+
import type {Cluster, ClusterNode, ClusterOptions, RedisOptions} from "ioredis";
|
|
20
21
|
import RedisClient from "ioredis";
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export type RedisInstance = RedisClient.Cluster | RedisClient.Redis;
|
|
25
|
-
|
|
22
|
+
import {v4 as uuid} from "uuid";
|
|
23
|
+
export type RedisInstance = RedisClient | Cluster
|
|
26
24
|
export interface Configuration {
|
|
27
25
|
/**
|
|
28
26
|
* Redis port
|
|
@@ -97,7 +95,7 @@ export class Redis implements Extension {
|
|
|
97
95
|
|
|
98
96
|
redlock: Redlock;
|
|
99
97
|
|
|
100
|
-
locks = new Map<string,
|
|
98
|
+
locks = new Map<string, {lock: Lock; release?: Promise<ExecutionResult>}>();
|
|
101
99
|
|
|
102
100
|
messagePrefix: Buffer;
|
|
103
101
|
|
|
@@ -138,7 +136,7 @@ export class Redis implements Extension {
|
|
|
138
136
|
this.sub.on("messageBuffer", this.handleIncomingMessage);
|
|
139
137
|
|
|
140
138
|
this.redlock = new Redlock([this.pub], {
|
|
141
|
-
|
|
139
|
+
driftFactor: 0.1
|
|
142
140
|
});
|
|
143
141
|
|
|
144
142
|
const identifierBuffer = Buffer.from(
|
|
@@ -214,7 +212,7 @@ export class Redis implements Extension {
|
|
|
214
212
|
.createSyncMessage()
|
|
215
213
|
.writeFirstSyncStepFor(document);
|
|
216
214
|
|
|
217
|
-
return this.pub.
|
|
215
|
+
return this.pub.publish(
|
|
218
216
|
this.pubKey(documentName),
|
|
219
217
|
this.encodeMessage(syncMessage.toUint8Array()),
|
|
220
218
|
);
|
|
@@ -228,7 +226,7 @@ export class Redis implements Extension {
|
|
|
228
226
|
documentName,
|
|
229
227
|
).writeQueryAwareness();
|
|
230
228
|
|
|
231
|
-
return this.pub.
|
|
229
|
+
return this.pub.publish(
|
|
232
230
|
this.pubKey(documentName),
|
|
233
231
|
this.encodeMessage(awarenessMessage.toUint8Array()),
|
|
234
232
|
);
|
|
@@ -238,49 +236,37 @@ export class Redis implements Extension {
|
|
|
238
236
|
* Before the document is stored, make sure to set a lock in Redis.
|
|
239
237
|
* That’s meant to avoid conflicts with other instances trying to store the document.
|
|
240
238
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
console.log("unable to acquire lock");
|
|
254
|
-
reject();
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
this.locks.set(this.lockKey(documentName), lock);
|
|
259
|
-
|
|
260
|
-
resolve(undefined);
|
|
261
|
-
},
|
|
262
|
-
);
|
|
263
|
-
});
|
|
264
|
-
}
|
|
239
|
+
async onStoreDocument({documentName}: onStoreDocumentPayload) {
|
|
240
|
+
// Attempt to acquire a lock and read lastReceivedTimestamp from Redis,
|
|
241
|
+
// to avoid conflict with other instances storing the same document.
|
|
242
|
+
const resource = this.lockKey(documentName)
|
|
243
|
+
const ttl = this.configuration.lockTimeout
|
|
244
|
+
const lock = await this.redlock.acquire([resource], ttl)
|
|
245
|
+
const oldLock = this.locks.get(resource)
|
|
246
|
+
if (oldLock) {
|
|
247
|
+
await oldLock.release
|
|
248
|
+
}
|
|
249
|
+
this.locks.set(resource, {lock})
|
|
250
|
+
}
|
|
265
251
|
|
|
266
252
|
/**
|
|
267
253
|
* Release the Redis lock, so other instances can store documents.
|
|
268
254
|
*/
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
255
|
+
async afterStoreDocument({documentName, socketId}: afterStoreDocumentPayload) {
|
|
256
|
+
const lockKey = this.lockKey(documentName)
|
|
257
|
+
const lock = this.locks.get(lockKey)
|
|
258
|
+
if (!lock) {
|
|
259
|
+
throw new Error(`Lock created in onStoreDocument not found in afterStoreDocument: ${lockKey}`)
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
// Always try to unlock and clean up the lock
|
|
263
|
+
lock.release = lock.lock.release()
|
|
264
|
+
await lock.release
|
|
265
|
+
} catch {
|
|
266
|
+
// Lock will expire on its own after timeout
|
|
267
|
+
} finally {
|
|
268
|
+
this.locks.delete(lockKey)
|
|
269
|
+
}
|
|
284
270
|
// if the change was initiated by a directConnection, we need to delay this hook to make sure sync can finish first.
|
|
285
271
|
// for provider connections, this usually happens in the onDisconnect hook
|
|
286
272
|
if (socketId === "server") {
|
|
@@ -326,7 +312,7 @@ export class Redis implements Extension {
|
|
|
326
312
|
documentName,
|
|
327
313
|
).createAwarenessUpdateMessage(awareness, changedClients);
|
|
328
314
|
|
|
329
|
-
return this.pub.
|
|
315
|
+
return this.pub.publish(
|
|
330
316
|
this.pubKey(documentName),
|
|
331
317
|
this.encodeMessage(message.toUint8Array()),
|
|
332
318
|
);
|
|
@@ -358,7 +344,7 @@ export class Redis implements Extension {
|
|
|
358
344
|
document,
|
|
359
345
|
undefined,
|
|
360
346
|
(reply) => {
|
|
361
|
-
return this.pub.
|
|
347
|
+
return this.pub.publish(
|
|
362
348
|
this.pubKey(document.name),
|
|
363
349
|
this.encodeMessage(reply),
|
|
364
350
|
);
|
|
@@ -418,7 +404,7 @@ export class Redis implements Extension {
|
|
|
418
404
|
data.documentName,
|
|
419
405
|
).writeBroadcastStateless(data.payload);
|
|
420
406
|
|
|
421
|
-
return this.pub.
|
|
407
|
+
return this.pub.publish(
|
|
422
408
|
this.pubKey(data.documentName),
|
|
423
409
|
this.encodeMessage(message.toUint8Array()),
|
|
424
410
|
);
|