Skip to content

Commit 829a1f5

Browse files
feat: broadcast and expect multiple acks
This feature was added in `[email protected]`: ```js io.timeout(1000).emit("some-event", (err, responses) => {   // ... }); ``` Thanks to this change, it will now work with multiple Socket.IO servers. Related: socketio/socket.io@8b20457
1 parent e546e4d commit 829a1f5

File tree

4 files changed

+369
-95
lines changed

4 files changed

+369
-95
lines changed

lib/index.ts

+99-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ enum EventType {
2121
FETCH_SOCKETS_RESPONSE,
2222
SERVER_SIDE_EMIT,
2323
SERVER_SIDE_EMIT_RESPONSE,
24+
BROADCAST_CLIENT_COUNT,
25+
BROADCAST_ACK,
2426
}
2527

2628
interface Request {
@@ -32,6 +34,12 @@ interface Request {
3234
responses: any[];
3335
}
3436

37+
interface AckRequest {
38+
type: EventType.BROADCAST;
39+
clientCountCallback: (clientCount: number) => void;
40+
ack: (...args: any[]) => void;
41+
}
42+
3543
/**
3644
* UID of an emitter using the `@socket.io/postgres-emitter` package
3745
*/
@@ -151,6 +159,7 @@ export class PostgresAdapter extends Adapter {
151159
private heartbeatTimer: NodeJS.Timeout | undefined;
152160
private cleanupTimer: NodeJS.Timeout | undefined;
153161
private requests: Map<string, Request> = new Map();
162+
private ackRequests: Map<string, AckRequest> = new Map();
154163

155164
/**
156165
* Adapter constructor.
@@ -271,12 +280,54 @@ export class PostgresAdapter extends Adapter {
271280
}
272281
case EventType.BROADCAST: {
273282
debug("broadcast with opts %j", document.data.opts);
274-
super.broadcast(
275-
document.data.packet,
276-
PostgresAdapter.deserializeOptions(document.data.opts)
277-
);
283+
284+
const withAck = document.data.requestId !== undefined;
285+
if (withAck) {
286+
super.broadcastWithAck(
287+
document.data.packet,
288+
PostgresAdapter.deserializeOptions(document.data.opts),
289+
(clientCount) => {
290+
debug("waiting for %d client acknowledgements", clientCount);
291+
this.publish({
292+
type: EventType.BROADCAST_CLIENT_COUNT,
293+
data: {
294+
requestId: document.data.requestId,
295+
clientCount,
296+
},
297+
});
298+
},
299+
(arg) => {
300+
debug("received acknowledgement with value %j", arg);
301+
this.publish({
302+
type: EventType.BROADCAST_ACK,
303+
data: {
304+
requestId: document.data.requestId,
305+
packet: arg,
306+
},
307+
});
308+
}
309+
);
310+
} else {
311+
super.broadcast(
312+
document.data.packet,
313+
PostgresAdapter.deserializeOptions(document.data.opts)
314+
);
315+
}
316+
break;
317+
}
318+
319+
case EventType.BROADCAST_CLIENT_COUNT: {
320+
const request = this.ackRequests.get(document.data.requestId);
321+
request?.clientCountCallback(document.data.clientCount);
322+
break;
323+
}
324+
325+
case EventType.BROADCAST_ACK: {
326+
const request = this.ackRequests.get(document.data.requestId);
327+
request?.ack(document.data.packet);
278328
break;
279329
}
330+
280331
case EventType.SOCKETS_JOIN: {
281332
debug("calling addSockets with opts %j", document.data.opts);
282333
super.addSockets(
@@ -285,6 +336,7 @@ export class PostgresAdapter extends Adapter {
285336
);
286337
break;
287338
}
339+
288340
case EventType.SOCKETS_LEAVE: {
289341
debug("calling delSockets with opts %j", document.data.opts);
290342
super.delSockets(
@@ -419,6 +471,7 @@ export class PostgresAdapter extends Adapter {
419471
if (
420472
[
421473
EventType.BROADCAST,
474+
EventType.BROADCAST_ACK,
422475
EventType.SERVER_SIDE_EMIT,
423476
EventType.SERVER_SIDE_EMIT_RESPONSE,
424477
].includes(document.type) &&
@@ -506,6 +559,48 @@ export class PostgresAdapter extends Adapter {
506559
});
507560
}
508561

562+
public broadcastWithAck(
563+
packet: any,
564+
opts: BroadcastOptions,
565+
clientCountCallback: (clientCount: number) => void,
566+
ack: (...args: any[]) => void
567+
) {
568+
const onlyLocal = opts?.flags?.local;
569+
if (!onlyLocal) {
570+
const requestId = randomId();
571+
572+
this.publish({
573+
type: EventType.BROADCAST,
574+
data: {
575+
packet,
576+
requestId,
577+
opts: PostgresAdapter.serializeOptions(opts),
578+
},
579+
});
580+
581+
this.ackRequests.set(requestId, {
582+
type: EventType.BROADCAST,
583+
clientCountCallback,
584+
ack,
585+
});
586+
587+
// we have no way to know at this level whether the server has received an acknowledgement from each client, so we
588+
// will simply clean up the ackRequests map after the given delay
589+
setTimeout(() => {
590+
this.ackRequests.delete(requestId);
591+
}, opts.flags!.timeout);
592+
}
593+
594+
// packets with binary contents are modified by the broadcast method, hence the nextTick()
595+
process.nextTick(() => {
596+
super.broadcastWithAck(packet, opts, clientCountCallback, ack);
597+
});
598+
}
599+
600+
public serverCount(): Promise<number> {
601+
return Promise.resolve(1 + this.nodesMap.size);
602+
}
603+
509604
addSockets(opts: BroadcastOptions, rooms: Room[]) {
510605
super.addSockets(opts, rooms);
511606

0 commit comments

Comments
 (0)