@@ -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
2628interface 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