Skip to content

Commit 3978e62

Browse files
committed
implement redis adapter for collab
1 parent 66b001a commit 3978e62

File tree

4 files changed

+233
-1
lines changed

4 files changed

+233
-1
lines changed

backend-services/collab-backend-service/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ PORT=5278
66
QUESTION_SERVICE_URL=http://peerprep-question-backend:5275/api/v1/question-service
77
HISTORY_SERVICE_URL=http://peerprep-history-service:5278/api/v1/history-service
88
VITE_MODE=dev
9+
# Optional Redis configuration for Socket.IO adapter
10+
COLLAB_REDIS_URL=redis://redis:6379
11+
COLLAB_REDIS_HOST=redis
12+
COLLAB_REDIS_PORT=6379
13+

backend-services/collab-backend-service/package-lock.json

Lines changed: 160 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend-services/collab-backend-service/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"mongoose": "^8.18.1",
2323
"socket.io": "^4.8.1",
2424
"typescript-eslint": "^8.46.2",
25-
"yjs": "^13.6.15"
25+
"yjs": "^13.6.15",
26+
"@socket.io/redis-adapter": "^8.2.0",
27+
"redis": "^4.7.0"
2628
},
2729
"devDependencies": {
2830
"@vitest/ui": "^3.2.4",

backend-services/collab-backend-service/src/sockets/collab.socket.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Server } from "socket.io";
2+
import { createAdapter } from "@socket.io/redis-adapter";
3+
import { createClient } from "redis";
24
import * as Y from "yjs";
35
import SessionService from "../services/session.service.js";
46
import { persistSessionHistory } from "../services/sessionHistory.service.js";
@@ -124,6 +126,39 @@ const getSessionSnapshot = (sessionId, socket) => {
124126
};
125127
};
126128

129+
const initialiseRedisAdapter = async () => {
130+
const redisUrl =
131+
process.env.COLLAB_REDIS_URL ?? process.env.REDIS_URL ?? null;
132+
const redisHost =
133+
process.env.COLLAB_REDIS_HOST ?? process.env.REDIS_HOST ?? null;
134+
135+
if (!redisUrl && !redisHost) {
136+
console.log(
137+
"[collab.socket][redis] Adapter disabled: no COLLAB_REDIS_URL or COLLAB_REDIS_HOST configured.",
138+
);
139+
return null;
140+
}
141+
142+
const pubClient = createClient({ url: redisUrl });
143+
const subClient = pubClient.duplicate();
144+
145+
pubClient.on("error", (error) => {
146+
console.error("[collab.socket][redis] Publisher error:", error);
147+
});
148+
subClient.on("error", (error) => {
149+
console.error("[collab.socket][redis] Subscriber error:", error);
150+
});
151+
152+
await Promise.all([pubClient.connect(), subClient.connect()]);
153+
154+
console.log("[collab.socket] Redis adapter connected.");
155+
156+
return {
157+
adapter: createAdapter(pubClient, subClient),
158+
clients: [pubClient, subClient],
159+
};
160+
};
161+
127162
const normaliseLanguage = (language) => {
128163
if (typeof language !== "string") {
129164
return null;
@@ -221,6 +256,28 @@ export const initSocket = (server) => {
221256
methods: ["GET", "POST"],
222257
},
223258
});
259+
const redisClients = [];
260+
261+
(async () => {
262+
try {
263+
const redisResources = await initialiseRedisAdapter();
264+
if (redisResources?.adapter) {
265+
io.adapter(redisResources.adapter);
266+
redisClients.push(...(redisResources.clients ?? []));
267+
console.log("[collab.socket] Redis adapter registered with Socket.IO.");
268+
}
269+
} catch (error) {
270+
console.error(
271+
"[collab.socket][redis] Failed to initialise adapter. Falling back to default adapter.",
272+
error,
273+
);
274+
}
275+
})().catch((error) => {
276+
console.error(
277+
"[collab.socket][redis] Unexpected error during adapter bootstrap:",
278+
error,
279+
);
280+
});
224281

225282
const runInactivitySweep = async () => {
226283
const now = Date.now();
@@ -693,6 +750,14 @@ export const initSocket = (server) => {
693750

694751
io.engine.on("close", () => {
695752
clearInterval(inactivityInterval);
753+
redisClients.forEach((client) => {
754+
client.quit?.().catch((error) => {
755+
console.warn(
756+
"[collab.socket][redis] Failed to close Redis client cleanly:",
757+
error,
758+
);
759+
});
760+
});
696761
});
697762

698763
return io;

0 commit comments

Comments
 (0)