Skip to content

Commit f66de11

Browse files
feat: send response to the requesting node only
Previously, a request would be sent to all listening nodes, on channel `${key}-request#${nsp}#` (e.g. "socket.io-request#/#"), and the other nodes would respond on a common channel `${key}-response#${nsp}#`, so every node get the response, instead of only the requesting node. This commit adds a new option: "publishOnSpecificResponseChannel". If it's set to true, then the other nodes will now respond on `${key}-response#${nsp}#${uid}#`, which is the channel specific to the requesting node, thus reducing the noise for the other nodes. To upgrade an existing deployment, users will need to upgrade all nodes to the latest version with publishOnSpecificResponseChannel = false, and then toggle the option on each node. Note: the option will default to true in the next major release Related: #407
1 parent aa681b3 commit f66de11

File tree

3 files changed

+54
-10
lines changed

3 files changed

+54
-10
lines changed

lib/index.ts

+46-8
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ export interface RedisAdapterOptions {
4242
* @default 5000
4343
*/
4444
requestsTimeout: number;
45+
/**
46+
* Whether to publish a response to the channel specific to the requesting node.
47+
*
48+
* - if true, the response will be published to `${key}-request#${nsp}#${uid}#`
49+
* - if false, the response will be published to `${key}-request#${nsp}#`
50+
*
51+
* This option currently defaults to false for backward compatibility, but will be set to true in the next major
52+
* release.
53+
*
54+
* @default false
55+
*/
56+
publishOnSpecificResponseChannel: boolean;
4557
}
4658

4759
/**
@@ -66,6 +78,7 @@ export function createAdapter(
6678
export class RedisAdapter extends Adapter {
6779
public readonly uid;
6880
public readonly requestsTimeout: number;
81+
public readonly publishOnSpecificResponseChannel: boolean;
6982

7083
private readonly channel: string;
7184
private readonly requestChannel: string;
@@ -92,12 +105,14 @@ export class RedisAdapter extends Adapter {
92105

93106
this.uid = uid2(6);
94107
this.requestsTimeout = opts.requestsTimeout || 5000;
108+
this.publishOnSpecificResponseChannel = !!opts.publishOnSpecificResponseChannel;
95109

96110
const prefix = opts.key || "socket.io";
97111

98112
this.channel = prefix + "#" + nsp.name + "#";
99113
this.requestChannel = prefix + "-request#" + this.nsp.name + "#";
100114
this.responseChannel = prefix + "-response#" + this.nsp.name + "#";
115+
const specificResponseChannel = this.responseChannel + this.uid + "#";
101116

102117
const onError = (err) => {
103118
if (err) {
@@ -115,7 +130,7 @@ export class RedisAdapter extends Adapter {
115130
true
116131
);
117132
this.subClient.subscribe(
118-
[this.requestChannel, this.responseChannel],
133+
[this.requestChannel, this.responseChannel, specificResponseChannel],
119134
(msg, channel) => {
120135
this.onrequest(channel, msg);
121136
}
@@ -125,7 +140,7 @@ export class RedisAdapter extends Adapter {
125140
this.subClient.on("pmessageBuffer", this.onmessage.bind(this));
126141

127142
this.subClient.subscribe(
128-
[this.requestChannel, this.responseChannel],
143+
[this.requestChannel, this.responseChannel, specificResponseChannel],
129144
onError
130145
);
131146
this.subClient.on("messageBuffer", this.onrequest.bind(this));
@@ -217,7 +232,7 @@ export class RedisAdapter extends Adapter {
217232
sockets: [...sockets],
218233
});
219234

220-
this.pubClient.publish(this.responseChannel, response);
235+
this.publishResponse(request, response);
221236
break;
222237

223238
case RequestType.ALL_ROOMS:
@@ -230,7 +245,7 @@ export class RedisAdapter extends Adapter {
230245
rooms: [...this.rooms.keys()],
231246
});
232247

233-
this.pubClient.publish(this.responseChannel, response);
248+
this.publishResponse(request, response);
234249
break;
235250

236251
case RequestType.REMOTE_JOIN:
@@ -253,7 +268,7 @@ export class RedisAdapter extends Adapter {
253268
requestId: request.requestId,
254269
});
255270

256-
this.pubClient.publish(this.responseChannel, response);
271+
this.publishResponse(request, response);
257272
break;
258273

259274
case RequestType.REMOTE_LEAVE:
@@ -276,7 +291,7 @@ export class RedisAdapter extends Adapter {
276291
requestId: request.requestId,
277292
});
278293

279-
this.pubClient.publish(this.responseChannel, response);
294+
this.publishResponse(request, response);
280295
break;
281296

282297
case RequestType.REMOTE_DISCONNECT:
@@ -299,7 +314,7 @@ export class RedisAdapter extends Adapter {
299314
requestId: request.requestId,
300315
});
301316

302-
this.pubClient.publish(this.responseChannel, response);
317+
this.publishResponse(request, response);
303318
break;
304319

305320
case RequestType.REMOTE_FETCH:
@@ -327,7 +342,7 @@ export class RedisAdapter extends Adapter {
327342
}),
328343
});
329344

330-
this.pubClient.publish(this.responseChannel, response);
345+
this.publishResponse(request, response);
331346
break;
332347

333348
case RequestType.SERVER_SIDE_EMIT:
@@ -366,6 +381,20 @@ export class RedisAdapter extends Adapter {
366381
}
367382
}
368383

384+
/**
385+
* Send the response to the requesting node
386+
* @param request
387+
* @param response
388+
* @private
389+
*/
390+
private publishResponse(request, response) {
391+
const responseChannel = this.publishOnSpecificResponseChannel
392+
? `${this.responseChannel}${request.uid}#`
393+
: this.responseChannel;
394+
debug("publishing response to channel %s", responseChannel);
395+
this.pubClient.publish(responseChannel, response);
396+
}
397+
369398
/**
370399
* Called on response from another node
371400
*
@@ -510,6 +539,7 @@ export class RedisAdapter extends Adapter {
510539

511540
const requestId = uid2(6);
512541
const request = JSON.stringify({
542+
uid: this.uid,
513543
requestId,
514544
type: RequestType.SOCKETS,
515545
rooms: [...rooms],
@@ -554,6 +584,7 @@ export class RedisAdapter extends Adapter {
554584

555585
const requestId = uid2(6);
556586
const request = JSON.stringify({
587+
uid: this.uid,
557588
requestId,
558589
type: RequestType.ALL_ROOMS,
559590
});
@@ -598,6 +629,7 @@ export class RedisAdapter extends Adapter {
598629
}
599630

600631
const request = JSON.stringify({
632+
uid: this.uid,
601633
requestId,
602634
type: RequestType.REMOTE_JOIN,
603635
sid: id,
@@ -641,6 +673,7 @@ export class RedisAdapter extends Adapter {
641673
}
642674

643675
const request = JSON.stringify({
676+
uid: this.uid,
644677
requestId,
645678
type: RequestType.REMOTE_LEAVE,
646679
sid: id,
@@ -684,6 +717,7 @@ export class RedisAdapter extends Adapter {
684717
}
685718

686719
const request = JSON.stringify({
720+
uid: this.uid,
687721
requestId,
688722
type: RequestType.REMOTE_DISCONNECT,
689723
sid: id,
@@ -729,6 +763,7 @@ export class RedisAdapter extends Adapter {
729763
const requestId = uid2(6);
730764

731765
const request = JSON.stringify({
766+
uid: this.uid,
732767
requestId,
733768
type: RequestType.REMOTE_FETCH,
734769
opts: {
@@ -766,6 +801,7 @@ export class RedisAdapter extends Adapter {
766801
}
767802

768803
const request = JSON.stringify({
804+
uid: this.uid,
769805
type: RequestType.REMOTE_JOIN,
770806
opts: {
771807
rooms: [...opts.rooms],
@@ -783,6 +819,7 @@ export class RedisAdapter extends Adapter {
783819
}
784820

785821
const request = JSON.stringify({
822+
uid: this.uid,
786823
type: RequestType.REMOTE_LEAVE,
787824
opts: {
788825
rooms: [...opts.rooms],
@@ -800,6 +837,7 @@ export class RedisAdapter extends Adapter {
800837
}
801838

802839
const request = JSON.stringify({
840+
uid: this.uid,
803841
type: RequestType.REMOTE_DISCONNECT,
804842
opts: {
805843
rooms: [...opts.rooms],

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
"main": "./dist/index.js",
1414
"types": "./dist/index.d.ts",
1515
"scripts": {
16-
"test": "npm run format:check && tsc && npm run test:redis-v4 && npm run test:redis-v3 && npm run test:ioredis",
16+
"test": "npm run format:check && tsc && npm run test:redis-v4 && npm run test:redis-v4-specific-channel && npm run test:redis-v3 && npm run test:ioredis",
1717
"test:redis-v4": "nyc mocha --bail --require ts-node/register test/index.ts",
18+
"test:redis-v4-specific-channel": "SPECIFIC_CHANNEL=1 nyc mocha --bail --require ts-node/register test/index.ts",
1819
"test:redis-v3": "REDIS_CLIENT=redis-v3 nyc mocha --bail --require ts-node/register test/index.ts",
1920
"test:ioredis": "REDIS_CLIENT=ioredis nyc mocha --bail --require ts-node/register test/index.ts",
2021
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'",

test/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,12 @@ function _create() {
403403
return async (nsp, fn?) => {
404404
const httpServer = createServer();
405405
const sio = new Server(httpServer);
406-
sio.adapter(createAdapter(await createClient(), await createClient()));
406+
sio.adapter(
407+
createAdapter(await createClient(), await createClient(), {
408+
publishOnSpecificResponseChannel:
409+
process.env.SPECIFIC_CHANNEL !== undefined,
410+
})
411+
);
407412
httpServer.listen((err) => {
408413
if (err) throw err; // abort tests
409414
if ("function" == typeof nsp) {

0 commit comments

Comments
 (0)