@@ -21,6 +21,8 @@ enum EventType {
21
21
FETCH_SOCKETS_RESPONSE ,
22
22
SERVER_SIDE_EMIT ,
23
23
SERVER_SIDE_EMIT_RESPONSE ,
24
+ BROADCAST_CLIENT_COUNT ,
25
+ BROADCAST_ACK ,
24
26
}
25
27
26
28
interface Request {
@@ -32,6 +34,12 @@ interface Request {
32
34
responses : any [ ] ;
33
35
}
34
36
37
+ interface AckRequest {
38
+ type : EventType . BROADCAST ;
39
+ clientCountCallback : ( clientCount : number ) => void ;
40
+ ack : ( ...args : any [ ] ) => void ;
41
+ }
42
+
35
43
/**
36
44
* UID of an emitter using the `@socket.io/postgres-emitter` package
37
45
*/
@@ -151,6 +159,7 @@ export class PostgresAdapter extends Adapter {
151
159
private heartbeatTimer : NodeJS . Timeout | undefined ;
152
160
private cleanupTimer : NodeJS . Timeout | undefined ;
153
161
private requests : Map < string , Request > = new Map ( ) ;
162
+ private ackRequests : Map < string , AckRequest > = new Map ( ) ;
154
163
155
164
/**
156
165
* Adapter constructor.
@@ -271,12 +280,54 @@ export class PostgresAdapter extends Adapter {
271
280
}
272
281
case EventType . BROADCAST : {
273
282
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 ) ;
278
328
break ;
279
329
}
330
+
280
331
case EventType . SOCKETS_JOIN : {
281
332
debug ( "calling addSockets with opts %j" , document . data . opts ) ;
282
333
super . addSockets (
@@ -285,6 +336,7 @@ export class PostgresAdapter extends Adapter {
285
336
) ;
286
337
break ;
287
338
}
339
+
288
340
case EventType . SOCKETS_LEAVE : {
289
341
debug ( "calling delSockets with opts %j" , document . data . opts ) ;
290
342
super . delSockets (
@@ -419,6 +471,7 @@ export class PostgresAdapter extends Adapter {
419
471
if (
420
472
[
421
473
EventType . BROADCAST ,
474
+ EventType . BROADCAST_ACK ,
422
475
EventType . SERVER_SIDE_EMIT ,
423
476
EventType . SERVER_SIDE_EMIT_RESPONSE ,
424
477
] . includes ( document . type ) &&
@@ -506,6 +559,48 @@ export class PostgresAdapter extends Adapter {
506
559
} ) ;
507
560
}
508
561
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
+
509
604
addSockets ( opts : BroadcastOptions , rooms : Room [ ] ) {
510
605
super . addSockets ( opts , rooms ) ;
511
606
0 commit comments