Skip to content

Commit 8ff0413

Browse files
refactor: use the ClusterAdapter class from socket.io-adapter package
The ClusterAdapter class has been moved to [1], so that this adapter only needs to implement to pub/sub mechanism. Also, [2] should reduce the number of "timeout reached: only x responses received out of y" errors, since the fetchSockets() requests will now succeed even if a server leaves the cluster. [1]: https://github.com/socketio/socket.io-adapter [2]: socketio/socket.io-adapter@0e23ff0 Related: #6
1 parent 44e10ae commit 8ff0413

File tree

5 files changed

+134
-615
lines changed

5 files changed

+134
-615
lines changed

lib/adapter.ts

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
1-
import type { PrivateSessionId, Session } from "socket.io-adapter";
2-
import { decode, encode } from "@msgpack/msgpack";
31
import {
4-
ClusterAdapter,
5-
ClusterAdapterOptions,
6-
ClusterMessage,
7-
MessageType,
8-
} from "./cluster-adapter";
2+
ClusterAdapterWithHeartbeat,
3+
type ClusterMessage,
4+
type PrivateSessionId,
5+
type Session,
6+
type ServerId,
7+
type ClusterResponse,
8+
} from "socket.io-adapter";
9+
import { decode, encode } from "@msgpack/msgpack";
910
import debugModule from "debug";
1011
import { hasBinary, XADD, XREAD } from "./util";
1112

1213
const debug = debugModule("socket.io-redis-streams-adapter");
1314

1415
const RESTORE_SESSION_MAX_XRANGE_CALLS = 100;
1516

16-
export interface RedisStreamsAdapterOptions extends ClusterAdapterOptions {
17+
// TODO ClusterAdapterOptions should be exported by the socket.io-adapter package
18+
interface ClusterAdapterOptions {
19+
/**
20+
* The number of ms between two heartbeats.
21+
* @default 5_000
22+
*/
23+
heartbeatInterval?: number;
24+
/**
25+
* The number of ms without heartbeat before we consider a node down.
26+
* @default 10_000
27+
*/
28+
heartbeatTimeout?: number;
29+
}
30+
31+
export interface RedisStreamsAdapterOptions {
1732
/**
1833
* The name of the Redis stream.
1934
*/
@@ -45,7 +60,7 @@ interface RawClusterMessage {
4560
*/
4661
export function createAdapter(
4762
redisClient: any,
48-
opts?: RedisStreamsAdapterOptions
63+
opts?: RedisStreamsAdapterOptions & ClusterAdapterOptions
4964
) {
5065
const namespaceToAdapters = new Map<string, RedisStreamsAdapter>();
5166
const options = Object.assign(
@@ -122,16 +137,20 @@ export function createAdapter(
122137
};
123138
}
124139

125-
class RedisStreamsAdapter extends ClusterAdapter {
140+
class RedisStreamsAdapter extends ClusterAdapterWithHeartbeat {
126141
readonly #redisClient: any;
127142
readonly #opts: Required<RedisStreamsAdapterOptions>;
128143

129-
constructor(nsp, redisClient, opts: Required<RedisStreamsAdapterOptions>) {
144+
constructor(
145+
nsp,
146+
redisClient,
147+
opts: Required<RedisStreamsAdapterOptions> & ClusterAdapterOptions
148+
) {
130149
super(nsp, opts);
131150
this.#redisClient = redisClient;
132151
this.#opts = opts;
133152

134-
this.initHeartbeat();
153+
this.init();
135154
}
136155

137156
override doPublish(message: ClusterMessage) {
@@ -145,25 +164,38 @@ class RedisStreamsAdapter extends ClusterAdapter {
145164
);
146165
}
147166

167+
protected doPublishResponse(
168+
requesterUid: ServerId,
169+
response: ClusterResponse
170+
): Promise<void> {
171+
// @ts-ignore
172+
return this.doPublish(response);
173+
}
174+
148175
static encode(message: ClusterMessage): RawClusterMessage {
149176
const rawMessage: RawClusterMessage = {
150177
uid: message.uid,
151178
nsp: message.nsp,
152179
type: message.type.toString(),
153180
};
154181

182+
// @ts-ignore
155183
if (message.data) {
184+
// TODO MessageType should be exported by the socket.io-adapter package
156185
const mayContainBinary = [
157-
MessageType.BROADCAST,
158-
MessageType.BROADCAST_ACK,
159-
MessageType.FETCH_SOCKETS_RESPONSE,
160-
MessageType.SERVER_SIDE_EMIT,
161-
MessageType.SERVER_SIDE_EMIT_RESPONSE,
186+
3, // MessageType.BROADCAST,
187+
8, // MessageType.FETCH_SOCKETS_RESPONSE,
188+
9, // MessageType.SERVER_SIDE_EMIT,
189+
10, // MessageType.SERVER_SIDE_EMIT_RESPONSE,
190+
12, // MessageType.BROADCAST_ACK,
162191
].includes(message.type);
163192

193+
// @ts-ignore
164194
if (mayContainBinary && hasBinary(message.data)) {
195+
// @ts-ignore
165196
rawMessage.data = Buffer.from(encode(message.data)).toString("base64");
166197
} else {
198+
// @ts-ignore
167199
rawMessage.data = JSON.stringify(message.data);
168200
}
169201
}
@@ -191,8 +223,10 @@ class RedisStreamsAdapter extends ClusterAdapter {
191223

192224
if (rawMessage.data) {
193225
if (rawMessage.data.startsWith("{")) {
226+
// @ts-ignore
194227
message.data = JSON.parse(rawMessage.data);
195228
} else {
229+
// @ts-ignore
196230
message.data = decode(Buffer.from(rawMessage.data, "base64")) as Record<
197231
string,
198232
unknown
@@ -261,6 +295,7 @@ class RedisStreamsAdapter extends ClusterAdapter {
261295
if (entry.message.nsp === this.nsp.name && entry.message.type === "3") {
262296
const message = RedisStreamsAdapter.decode(entry.message);
263297

298+
// @ts-ignore
264299
if (shouldIncludePacket(session.rooms, message.data.opts)) {
265300
// @ts-ignore
266301
session.missedPackets.push(message.data.packet.data);

0 commit comments

Comments
 (0)