@socket.io/redis-streams-adapter 0.2.0 → 0.2.1
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/README.md +81 -0
- package/dist/adapter.d.ts +2 -13
- package/dist/adapter.js +13 -18
- package/dist/util.d.ts +12 -0
- package/dist/util.js +66 -13
- package/package.json +4 -3
- package/dist/cluster-adapter-v2.d.ts +0 -184
- package/dist/cluster-adapter-v2.js +0 -488
- package/dist/cluster-adapter.d.ts +0 -54
- package/dist/cluster-adapter.js +0 -420
package/dist/cluster-adapter.js
DELETED
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ClusterAdapter = exports.MessageType = void 0;
|
|
4
|
-
const socket_io_adapter_1 = require("socket.io-adapter");
|
|
5
|
-
const debug_1 = require("debug");
|
|
6
|
-
const util_1 = require("./util");
|
|
7
|
-
const debug = (0, debug_1.default)("socket.io-adapter");
|
|
8
|
-
const EMITTER_UID = "emitter";
|
|
9
|
-
const DEFAULT_TIMEOUT = 5000;
|
|
10
|
-
var MessageType;
|
|
11
|
-
(function (MessageType) {
|
|
12
|
-
MessageType[MessageType["INITIAL_HEARTBEAT"] = 1] = "INITIAL_HEARTBEAT";
|
|
13
|
-
MessageType[MessageType["HEARTBEAT"] = 2] = "HEARTBEAT";
|
|
14
|
-
MessageType[MessageType["BROADCAST"] = 3] = "BROADCAST";
|
|
15
|
-
MessageType[MessageType["SOCKETS_JOIN"] = 4] = "SOCKETS_JOIN";
|
|
16
|
-
MessageType[MessageType["SOCKETS_LEAVE"] = 5] = "SOCKETS_LEAVE";
|
|
17
|
-
MessageType[MessageType["DISCONNECT_SOCKETS"] = 6] = "DISCONNECT_SOCKETS";
|
|
18
|
-
MessageType[MessageType["FETCH_SOCKETS"] = 7] = "FETCH_SOCKETS";
|
|
19
|
-
MessageType[MessageType["FETCH_SOCKETS_RESPONSE"] = 8] = "FETCH_SOCKETS_RESPONSE";
|
|
20
|
-
MessageType[MessageType["SERVER_SIDE_EMIT"] = 9] = "SERVER_SIDE_EMIT";
|
|
21
|
-
MessageType[MessageType["SERVER_SIDE_EMIT_RESPONSE"] = 10] = "SERVER_SIDE_EMIT_RESPONSE";
|
|
22
|
-
MessageType[MessageType["BROADCAST_CLIENT_COUNT"] = 11] = "BROADCAST_CLIENT_COUNT";
|
|
23
|
-
MessageType[MessageType["BROADCAST_ACK"] = 12] = "BROADCAST_ACK";
|
|
24
|
-
})(MessageType = exports.MessageType || (exports.MessageType = {}));
|
|
25
|
-
function encodeOptions(opts) {
|
|
26
|
-
return {
|
|
27
|
-
rooms: [...opts.rooms],
|
|
28
|
-
except: [...opts.except],
|
|
29
|
-
flags: opts.flags,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
function decodeOptions(opts) {
|
|
33
|
-
return {
|
|
34
|
-
rooms: new Set(opts.rooms),
|
|
35
|
-
except: new Set(opts.except),
|
|
36
|
-
flags: opts.flags,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
class ClusterAdapter extends socket_io_adapter_1.Adapter {
|
|
40
|
-
#opts;
|
|
41
|
-
#uid;
|
|
42
|
-
#heartbeatTimer;
|
|
43
|
-
#nodesMap = new Map(); // uid => timestamp of last message
|
|
44
|
-
#requests = new Map();
|
|
45
|
-
#ackRequests = new Map();
|
|
46
|
-
constructor(nsp, opts) {
|
|
47
|
-
super(nsp);
|
|
48
|
-
this.#opts = opts;
|
|
49
|
-
this.#uid = (0, util_1.randomId)();
|
|
50
|
-
}
|
|
51
|
-
initHeartbeat() {
|
|
52
|
-
this.#publish({
|
|
53
|
-
type: MessageType.INITIAL_HEARTBEAT,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
#scheduleHeartbeat() {
|
|
57
|
-
if (this.#heartbeatTimer) {
|
|
58
|
-
clearTimeout(this.#heartbeatTimer);
|
|
59
|
-
}
|
|
60
|
-
this.#heartbeatTimer = setTimeout(() => {
|
|
61
|
-
this.#publish({
|
|
62
|
-
type: MessageType.HEARTBEAT,
|
|
63
|
-
});
|
|
64
|
-
}, this.#opts.heartbeatInterval);
|
|
65
|
-
}
|
|
66
|
-
close() {
|
|
67
|
-
clearTimeout(this.#heartbeatTimer);
|
|
68
|
-
}
|
|
69
|
-
async onMessage(message, offset) {
|
|
70
|
-
if (message.uid === this.#uid) {
|
|
71
|
-
return debug("ignore message from self");
|
|
72
|
-
}
|
|
73
|
-
if (message.uid && message.uid !== EMITTER_UID) {
|
|
74
|
-
this.#nodesMap.set(message.uid, Date.now());
|
|
75
|
-
}
|
|
76
|
-
debug("new event of type %d from %s", message.type, message.uid);
|
|
77
|
-
switch (message.type) {
|
|
78
|
-
case MessageType.INITIAL_HEARTBEAT:
|
|
79
|
-
this.#publish({
|
|
80
|
-
type: MessageType.HEARTBEAT,
|
|
81
|
-
});
|
|
82
|
-
break;
|
|
83
|
-
case MessageType.BROADCAST: {
|
|
84
|
-
const withAck = message.data.requestId !== undefined;
|
|
85
|
-
if (withAck) {
|
|
86
|
-
super.broadcastWithAck(message.data.packet, decodeOptions(message.data.opts), (clientCount) => {
|
|
87
|
-
debug("waiting for %d client acknowledgements", clientCount);
|
|
88
|
-
this.#publish({
|
|
89
|
-
type: MessageType.BROADCAST_CLIENT_COUNT,
|
|
90
|
-
data: {
|
|
91
|
-
requestId: message.data.requestId,
|
|
92
|
-
clientCount,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
}, (arg) => {
|
|
96
|
-
debug("received acknowledgement with value %j", arg);
|
|
97
|
-
this.#publish({
|
|
98
|
-
type: MessageType.BROADCAST_ACK,
|
|
99
|
-
data: {
|
|
100
|
-
requestId: message.data.requestId,
|
|
101
|
-
packet: arg,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
const packet = message.data.packet;
|
|
108
|
-
const opts = decodeOptions(message.data.opts);
|
|
109
|
-
this.#addOffsetIfNecessary(packet, opts, offset);
|
|
110
|
-
super.broadcast(packet, opts);
|
|
111
|
-
}
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
case MessageType.BROADCAST_CLIENT_COUNT: {
|
|
115
|
-
const request = this.#ackRequests.get(message.data.requestId);
|
|
116
|
-
request?.clientCountCallback(message.data.clientCount);
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
case MessageType.BROADCAST_ACK: {
|
|
120
|
-
const request = this.#ackRequests.get(message.data.requestId);
|
|
121
|
-
request?.ack(message.data.packet);
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
case MessageType.SOCKETS_JOIN:
|
|
125
|
-
super.addSockets(decodeOptions(message.data.opts), message.data.rooms);
|
|
126
|
-
break;
|
|
127
|
-
case MessageType.SOCKETS_LEAVE:
|
|
128
|
-
super.delSockets(decodeOptions(message.data.opts), message.data.rooms);
|
|
129
|
-
break;
|
|
130
|
-
case MessageType.DISCONNECT_SOCKETS:
|
|
131
|
-
super.disconnectSockets(decodeOptions(message.data.opts), message.data.close);
|
|
132
|
-
break;
|
|
133
|
-
case MessageType.FETCH_SOCKETS: {
|
|
134
|
-
debug("calling fetchSockets with opts %j", message.data.opts);
|
|
135
|
-
const localSockets = await super.fetchSockets(decodeOptions(message.data.opts));
|
|
136
|
-
this.#publish({
|
|
137
|
-
type: MessageType.FETCH_SOCKETS_RESPONSE,
|
|
138
|
-
data: {
|
|
139
|
-
requestId: message.data.requestId,
|
|
140
|
-
sockets: localSockets.map((socket) => {
|
|
141
|
-
// remove sessionStore from handshake, as it may contain circular references
|
|
142
|
-
const { sessionStore, ...handshake } = socket.handshake;
|
|
143
|
-
return {
|
|
144
|
-
id: socket.id,
|
|
145
|
-
handshake,
|
|
146
|
-
rooms: [...socket.rooms],
|
|
147
|
-
data: socket.data,
|
|
148
|
-
};
|
|
149
|
-
}),
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
case MessageType.FETCH_SOCKETS_RESPONSE: {
|
|
155
|
-
const requestId = message.data.requestId;
|
|
156
|
-
const request = this.#requests.get(requestId);
|
|
157
|
-
if (!request) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
request.current++;
|
|
161
|
-
message.data.sockets.forEach((socket) => request.responses.push(socket));
|
|
162
|
-
if (request.current === request.expected) {
|
|
163
|
-
clearTimeout(request.timeout);
|
|
164
|
-
request.resolve(request.responses);
|
|
165
|
-
this.#requests.delete(requestId);
|
|
166
|
-
}
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case MessageType.SERVER_SIDE_EMIT: {
|
|
170
|
-
const packet = message.data.packet;
|
|
171
|
-
const withAck = message.data.requestId !== undefined;
|
|
172
|
-
if (!withAck) {
|
|
173
|
-
this.nsp._onServerSideEmit(packet);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
let called = false;
|
|
177
|
-
const callback = (arg) => {
|
|
178
|
-
// only one argument is expected
|
|
179
|
-
if (called) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
called = true;
|
|
183
|
-
debug("calling acknowledgement with %j", arg);
|
|
184
|
-
this.#publish({
|
|
185
|
-
type: MessageType.SERVER_SIDE_EMIT_RESPONSE,
|
|
186
|
-
data: {
|
|
187
|
-
requestId: message.data.requestId,
|
|
188
|
-
packet: arg,
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
packet.push(callback);
|
|
193
|
-
this.nsp._onServerSideEmit(packet);
|
|
194
|
-
break;
|
|
195
|
-
}
|
|
196
|
-
case MessageType.SERVER_SIDE_EMIT_RESPONSE: {
|
|
197
|
-
const requestId = message.data.requestId;
|
|
198
|
-
const request = this.#requests.get(requestId);
|
|
199
|
-
if (!request) {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
request.current++;
|
|
203
|
-
request.responses.push(message.data.packet);
|
|
204
|
-
if (request.current === request.expected) {
|
|
205
|
-
clearTimeout(request.timeout);
|
|
206
|
-
request.resolve(null, request.responses);
|
|
207
|
-
this.#requests.delete(requestId);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
async broadcast(packet, opts) {
|
|
213
|
-
const onlyLocal = opts.flags?.local;
|
|
214
|
-
if (!onlyLocal) {
|
|
215
|
-
try {
|
|
216
|
-
const offset = await this.#publish({
|
|
217
|
-
type: MessageType.BROADCAST,
|
|
218
|
-
data: {
|
|
219
|
-
packet,
|
|
220
|
-
opts: encodeOptions(opts),
|
|
221
|
-
},
|
|
222
|
-
});
|
|
223
|
-
this.#addOffsetIfNecessary(packet, opts, offset);
|
|
224
|
-
}
|
|
225
|
-
catch (e) {
|
|
226
|
-
return debug("error while broadcasting message: %s", e.message);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
super.broadcast(packet, opts);
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Adds an offset at the end of the data array in order to allow the client to receive any missed packets when it
|
|
233
|
-
* reconnects after a temporary disconnection.
|
|
234
|
-
*
|
|
235
|
-
* @param packet
|
|
236
|
-
* @param opts
|
|
237
|
-
* @param offset
|
|
238
|
-
* @private
|
|
239
|
-
*/
|
|
240
|
-
#addOffsetIfNecessary(packet, opts, offset) {
|
|
241
|
-
if (!this.nsp.server.opts.connectionStateRecovery) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
const isEventPacket = packet.type === 2;
|
|
245
|
-
// packets with acknowledgement are not stored because the acknowledgement function cannot be serialized and
|
|
246
|
-
// restored on another server upon reconnection
|
|
247
|
-
const withoutAcknowledgement = packet.id === undefined;
|
|
248
|
-
const notVolatile = opts.flags?.volatile === undefined;
|
|
249
|
-
if (isEventPacket && withoutAcknowledgement && notVolatile) {
|
|
250
|
-
packet.data.push(offset);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
broadcastWithAck(packet, opts, clientCountCallback, ack) {
|
|
254
|
-
const onlyLocal = opts?.flags?.local;
|
|
255
|
-
if (!onlyLocal) {
|
|
256
|
-
const requestId = (0, util_1.randomId)();
|
|
257
|
-
this.#publish({
|
|
258
|
-
type: MessageType.BROADCAST,
|
|
259
|
-
data: {
|
|
260
|
-
packet,
|
|
261
|
-
requestId,
|
|
262
|
-
opts: encodeOptions(opts),
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
this.#ackRequests.set(requestId, {
|
|
266
|
-
clientCountCallback,
|
|
267
|
-
ack,
|
|
268
|
-
});
|
|
269
|
-
// we have no way to know at this level whether the server has received an acknowledgement from each client, so we
|
|
270
|
-
// will simply clean up the ackRequests map after the given delay
|
|
271
|
-
setTimeout(() => {
|
|
272
|
-
this.#ackRequests.delete(requestId);
|
|
273
|
-
}, opts.flags.timeout);
|
|
274
|
-
}
|
|
275
|
-
super.broadcastWithAck(packet, opts, clientCountCallback, ack);
|
|
276
|
-
}
|
|
277
|
-
serverCount() {
|
|
278
|
-
return Promise.resolve(1 + this.#nodesMap.size);
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
*
|
|
282
|
-
* @param opts
|
|
283
|
-
* @param rooms
|
|
284
|
-
*/
|
|
285
|
-
addSockets(opts, rooms) {
|
|
286
|
-
super.addSockets(opts, rooms);
|
|
287
|
-
const onlyLocal = opts.flags?.local;
|
|
288
|
-
if (onlyLocal) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
this.#publish({
|
|
292
|
-
type: MessageType.SOCKETS_JOIN,
|
|
293
|
-
data: {
|
|
294
|
-
opts: encodeOptions(opts),
|
|
295
|
-
rooms,
|
|
296
|
-
},
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
delSockets(opts, rooms) {
|
|
300
|
-
super.delSockets(opts, rooms);
|
|
301
|
-
const onlyLocal = opts.flags?.local;
|
|
302
|
-
if (onlyLocal) {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
this.#publish({
|
|
306
|
-
type: MessageType.SOCKETS_LEAVE,
|
|
307
|
-
data: {
|
|
308
|
-
opts: encodeOptions(opts),
|
|
309
|
-
rooms,
|
|
310
|
-
},
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
disconnectSockets(opts, close) {
|
|
314
|
-
super.disconnectSockets(opts, close);
|
|
315
|
-
const onlyLocal = opts.flags?.local;
|
|
316
|
-
if (onlyLocal) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
this.#publish({
|
|
320
|
-
type: MessageType.DISCONNECT_SOCKETS,
|
|
321
|
-
data: {
|
|
322
|
-
opts: encodeOptions(opts),
|
|
323
|
-
close,
|
|
324
|
-
},
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
#getExpectedResponseCount() {
|
|
328
|
-
this.#nodesMap.forEach((lastSeen, uid) => {
|
|
329
|
-
const nodeSeemsDown = Date.now() - lastSeen > this.#opts.heartbeatTimeout;
|
|
330
|
-
if (nodeSeemsDown) {
|
|
331
|
-
debug("node %s seems down", uid);
|
|
332
|
-
this.#nodesMap.delete(uid);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
return this.#nodesMap.size;
|
|
336
|
-
}
|
|
337
|
-
async fetchSockets(opts) {
|
|
338
|
-
const localSockets = await super.fetchSockets(opts);
|
|
339
|
-
const expectedResponseCount = this.#getExpectedResponseCount();
|
|
340
|
-
if (opts.flags?.local || expectedResponseCount === 0) {
|
|
341
|
-
return localSockets;
|
|
342
|
-
}
|
|
343
|
-
const requestId = (0, util_1.randomId)();
|
|
344
|
-
return new Promise((resolve, reject) => {
|
|
345
|
-
const timeout = setTimeout(() => {
|
|
346
|
-
const storedRequest = this.#requests.get(requestId);
|
|
347
|
-
if (storedRequest) {
|
|
348
|
-
reject(new Error(`timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}`));
|
|
349
|
-
this.#requests.delete(requestId);
|
|
350
|
-
}
|
|
351
|
-
}, opts.flags.timeout || DEFAULT_TIMEOUT);
|
|
352
|
-
const storedRequest = {
|
|
353
|
-
type: MessageType.FETCH_SOCKETS,
|
|
354
|
-
resolve,
|
|
355
|
-
timeout,
|
|
356
|
-
current: 0,
|
|
357
|
-
expected: expectedResponseCount,
|
|
358
|
-
responses: localSockets,
|
|
359
|
-
};
|
|
360
|
-
this.#requests.set(requestId, storedRequest);
|
|
361
|
-
this.#publish({
|
|
362
|
-
type: MessageType.FETCH_SOCKETS,
|
|
363
|
-
data: {
|
|
364
|
-
opts: encodeOptions(opts),
|
|
365
|
-
requestId,
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
serverSideEmit(packet) {
|
|
371
|
-
const withAck = typeof packet[packet.length - 1] === "function";
|
|
372
|
-
if (!withAck) {
|
|
373
|
-
return this.#publish({
|
|
374
|
-
type: MessageType.SERVER_SIDE_EMIT,
|
|
375
|
-
data: {
|
|
376
|
-
packet,
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
const ack = packet.pop();
|
|
381
|
-
const expectedResponseCount = this.#getExpectedResponseCount();
|
|
382
|
-
debug('waiting for %d responses to "serverSideEmit" request', expectedResponseCount);
|
|
383
|
-
if (expectedResponseCount <= 0) {
|
|
384
|
-
return ack(null, []);
|
|
385
|
-
}
|
|
386
|
-
const requestId = (0, util_1.randomId)();
|
|
387
|
-
const timeout = setTimeout(() => {
|
|
388
|
-
const storedRequest = this.#requests.get(requestId);
|
|
389
|
-
if (storedRequest) {
|
|
390
|
-
ack(new Error(`timeout reached: only ${storedRequest.current} responses received out of ${storedRequest.expected}`), storedRequest.responses);
|
|
391
|
-
this.#requests.delete(requestId);
|
|
392
|
-
}
|
|
393
|
-
}, DEFAULT_TIMEOUT);
|
|
394
|
-
const storedRequest = {
|
|
395
|
-
type: MessageType.SERVER_SIDE_EMIT,
|
|
396
|
-
resolve: ack,
|
|
397
|
-
timeout,
|
|
398
|
-
current: 0,
|
|
399
|
-
expected: expectedResponseCount,
|
|
400
|
-
responses: [],
|
|
401
|
-
};
|
|
402
|
-
this.#requests.set(requestId, storedRequest);
|
|
403
|
-
this.#publish({
|
|
404
|
-
type: MessageType.SERVER_SIDE_EMIT,
|
|
405
|
-
data: {
|
|
406
|
-
requestId,
|
|
407
|
-
packet,
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
#publish(message) {
|
|
412
|
-
this.#scheduleHeartbeat();
|
|
413
|
-
return this.doPublish({
|
|
414
|
-
uid: this.#uid,
|
|
415
|
-
nsp: this.nsp.name,
|
|
416
|
-
...message,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
exports.ClusterAdapter = ClusterAdapter;
|