Skip to content

Commit 14a2277

Browse files
authored
Merge pull request #78 from ruiqi7/feature/matching-websocket
Set up queues per match criteria
2 parents 2db16f9 + f432a5e commit 14a2277

File tree

8 files changed

+120
-94
lines changed

8 files changed

+120
-94
lines changed

backend/matching-service/config/rabbitmq.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,69 @@ import amqplib, { Connection } from "amqplib";
22
import dotenv from "dotenv";
33
import { matchUsers } from "../src/utils/mq_utils";
44
import { MatchRequestItem } from "../src/handlers/matchHandler";
5+
import { Complexities, Categories, Languages } from "../src/utils/constants";
56

67
dotenv.config();
78

89
let mrConnection: Connection;
9-
const queue = "match_requests";
10+
const queues: string[] = [];
11+
const pendingQueueRequests = new Map<string, Map<string, MatchRequestItem>>();
1012

11-
export const connectRabbitMq = async () => {
13+
const initQueueNames = () => {
14+
for (const complexity of Object.values(Complexities)) {
15+
for (const category of Object.values(Categories)) {
16+
for (const language of Object.values(Languages)) {
17+
queues.push(`${complexity}_${category}_${language}`);
18+
}
19+
}
20+
}
21+
};
22+
23+
const setUpQueue = async (queueName: string) => {
24+
const consumerChannel = await mrConnection.createChannel();
25+
await consumerChannel.assertQueue(queueName);
26+
27+
consumerChannel.consume(queueName, (msg) => {
28+
if (msg !== null) {
29+
matchUsers(queueName, msg.content.toString());
30+
consumerChannel.ack(msg);
31+
}
32+
});
33+
};
34+
35+
export const connectToRabbitMq = async () => {
1236
try {
37+
initQueueNames();
1338
mrConnection = await amqplib.connect(`${process.env.RABBITMQ_ADDR}`);
14-
const consumerChannel = await mrConnection.createChannel();
15-
await consumerChannel.assertQueue(queue);
16-
17-
consumerChannel.consume(queue, (msg) => {
18-
if (msg !== null) {
19-
matchUsers(msg.content.toString());
20-
consumerChannel.ack(msg);
21-
}
22-
});
39+
for (const queue of queues) {
40+
await setUpQueue(queue);
41+
pendingQueueRequests.set(queue, new Map<string, MatchRequestItem>());
42+
}
2343
} catch (error) {
2444
console.error(error);
2545
process.exit(1);
2646
}
2747
};
2848

29-
export const sendRabbitMq = async (
49+
export const sendToQueue = async (
50+
complexity: string,
51+
category: string,
52+
language: string,
3053
data: MatchRequestItem
3154
): Promise<boolean> => {
3255
try {
56+
const queueName = `${complexity}_${category}_${language}`;
3357
const senderChannel = await mrConnection.createChannel();
34-
senderChannel.sendToQueue(queue, Buffer.from(JSON.stringify(data)));
35-
console.log("Sent to queue:", JSON.stringify(data));
58+
senderChannel.sendToQueue(queueName, Buffer.from(JSON.stringify(data)));
3659
return true;
3760
} catch (error) {
3861
console.log(error);
3962
return false;
4063
}
4164
};
65+
66+
export const getPendingRequests = (
67+
queueName: string
68+
): Map<string, MatchRequestItem> => {
69+
return pendingQueueRequests.get(queueName)!;
70+
};

backend/matching-service/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import http from "http";
22
import app, { allowedOrigins } from "./app.ts";
33
import { handleWebsocketMatchEvents } from "./src/handlers/websocketHandler.ts";
44
import { Server } from "socket.io";
5-
import { connectRabbitMq } from "./config/rabbitmq.ts";
5+
import { connectToRabbitMq } from "./config/rabbitmq.ts";
66

77
const server = http.createServer(app);
88
export const io = new Server(server, {
@@ -20,7 +20,7 @@ io.on("connection", (socket) => {
2020
const PORT = process.env.SERVICE_PORT || 3002;
2121

2222
if (process.env.NODE_ENV !== "test") {
23-
connectRabbitMq()
23+
connectToRabbitMq()
2424
.then(() => {
2525
console.log("RabbitMq connected!");
2626

backend/matching-service/src/handlers/matchHandler.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { v4 as uuidv4 } from "uuid";
2-
import { sendRabbitMq } from "../../config/rabbitmq";
2+
import { sendToQueue } from "../../config/rabbitmq";
33
import { sendMatchFound } from "./websocketHandler";
44

55
interface Match {
@@ -16,18 +16,15 @@ export interface MatchUser {
1616

1717
export interface MatchRequest {
1818
user: MatchUser;
19-
complexities: string[];
20-
categories: string[];
21-
languages: string[];
19+
complexity: string;
20+
category: string;
21+
language: string;
2222
timeout: number;
2323
}
2424

2525
export interface MatchRequestItem {
2626
id: string;
2727
user: MatchUser;
28-
complexities: string[];
29-
categories: string[];
30-
languages: string[];
3128
sentTimestamp: number;
3229
ttlInSecs: number;
3330
rejectedPartnerId?: string;
@@ -40,20 +37,17 @@ export const sendMatchRequest = async (
4037
requestId: string,
4138
rejectedPartnerId?: string
4239
): Promise<boolean> => {
43-
const { user, complexities, categories, languages, timeout } = matchRequest;
40+
const { user, complexity, category, language, timeout } = matchRequest;
4441

4542
const matchItem: MatchRequestItem = {
4643
id: requestId,
4744
user: user,
48-
complexities: complexities,
49-
categories: categories,
50-
languages: languages,
5145
sentTimestamp: Date.now(),
5246
ttlInSecs: timeout,
5347
rejectedPartnerId: rejectedPartnerId,
5448
};
5549

56-
const sent = await sendRabbitMq(matchItem);
50+
const sent = await sendToQueue(complexity, category, language, matchItem);
5751
return sent;
5852
};
5953

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export enum Complexities {
2+
EASY = "Easy",
3+
MEDIUM = "Medium",
4+
HARD = "Hard",
5+
}
6+
7+
export enum Categories {
8+
STRINGS = "Strings",
9+
ALGORITHMS = "Algorithms",
10+
DATA_STRUCTURES = "Data Structures",
11+
BIT_MANIPULATION = "Bit Manipulation",
12+
RECURSION = "Recursion",
13+
DYNAMIC_PROGRAMMING = "Dynamic Programming",
14+
ARRAYS = "Arrays",
15+
TREE = "Tree",
16+
}
17+
18+
export enum Languages {
19+
PYTHON = "Python",
20+
JAVA = "Java",
21+
C = "C",
22+
}
Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1+
import { getPendingRequests } from "../../config/rabbitmq";
12
import { createMatch, MatchRequestItem } from "../handlers/matchHandler";
23
import { isActiveRequest, isUserConnected } from "../handlers/websocketHandler";
34

4-
const matchingRequests = new Map<string, MatchRequestItem>();
5-
6-
export const matchUsers = (newRequest: string) => {
5+
export const matchUsers = (queueName: string, newRequest: string) => {
6+
const pendingRequests = getPendingRequests(queueName);
77
const newRequestJson = JSON.parse(newRequest) as MatchRequestItem;
88
const newRequestUid = newRequestJson.user.id;
9-
for (const [uid, pendingRequest] of matchingRequests) {
9+
10+
for (const [uid, pendingRequest] of pendingRequests) {
1011
if (
1112
isExpired(pendingRequest) ||
1213
!isUserConnected(uid) ||
1314
!isActiveRequest(uid, pendingRequest.id) ||
1415
uid === newRequestUid
1516
) {
16-
matchingRequests.delete(uid);
17+
pendingRequests.delete(uid);
1718
continue;
1819
}
20+
1921
if (
2022
isExpired(newRequestJson) ||
2123
!isUserConnected(newRequestUid) ||
@@ -31,34 +33,13 @@ export const matchUsers = (newRequest: string) => {
3133
continue;
3234
}
3335

34-
if (isMatch(newRequestJson, pendingRequest)) {
35-
matchingRequests.delete(uid);
36-
createMatch(pendingRequest, newRequestJson);
37-
console.log(
38-
"Matched users:",
39-
JSON.stringify(newRequestJson),
40-
JSON.stringify(pendingRequest)
41-
);
42-
return;
43-
}
36+
pendingRequests.delete(uid);
37+
createMatch(pendingRequest, newRequestJson);
38+
return;
4439
}
45-
matchingRequests.set(newRequestUid, newRequestJson);
40+
pendingRequests.set(newRequestUid, newRequestJson);
4641
};
4742

4843
const isExpired = (data: MatchRequestItem): boolean => {
4944
return Date.now() - data.sentTimestamp >= data.ttlInSecs * 1000;
5045
};
51-
52-
const isMatch = (req1: MatchRequestItem, req2: MatchRequestItem): boolean => {
53-
const hasCommonComplexity = req1.complexities.some((elem) =>
54-
req2.complexities.includes(elem)
55-
);
56-
const hasCommonCategory = req1.categories.some((elem) =>
57-
req2.categories.includes(elem)
58-
);
59-
const hasCommonLanguage = req1.languages.some((elem) =>
60-
req2.languages.includes(elem)
61-
);
62-
63-
return hasCommonComplexity && hasCommonCategory && hasCommonLanguage;
64-
};

frontend/src/contexts/MatchContext.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ type MatchUser = {
2525
};
2626

2727
type MatchCriteria = {
28-
complexities: string[];
29-
categories: string[];
30-
languages: string[];
28+
complexity: string;
29+
category: string;
30+
language: string;
3131
timeout: number;
3232
};
3333

@@ -69,9 +69,9 @@ enum MatchPaths {
6969

7070
type MatchContextType = {
7171
findMatch: (
72-
complexities: string[],
73-
categories: string[],
74-
languages: string[],
72+
complexity: string,
73+
category: string,
74+
language: string,
7575
timeout: number
7676
) => void;
7777
stopMatch: () => void;
@@ -311,9 +311,9 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
311311
};
312312

313313
const findMatch = (
314-
complexities: string[],
315-
categories: string[],
316-
languages: string[],
314+
complexity: string,
315+
category: string,
316+
language: string,
317317
timeout: number
318318
) => {
319319
if (!matchUser) {
@@ -332,19 +332,19 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
332332
MatchEvents.MATCH_REQUEST,
333333
{
334334
user: matchUser,
335-
complexities: complexities,
336-
categories: categories,
337-
languages: languages,
335+
complexity: complexity,
336+
category: category,
337+
language: language,
338338
timeout: timeout,
339339
},
340340
(requested: boolean) => {
341341
clearTimeout(requestTimeout);
342342
setTimeout(() => setLoading(false), 500);
343343
if (requested) {
344344
setMatchCriteria({
345-
complexities,
346-
categories,
347-
languages,
345+
complexity: complexity,
346+
category: category,
347+
language: language,
348348
timeout,
349349
});
350350
appNavigate(MatchPaths.MATCHING);
@@ -402,9 +402,9 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
402402

403403
const rematchRequest = {
404404
user: matchUser,
405-
complexities: matchCriteria.complexities,
406-
categories: matchCriteria.categories,
407-
languages: matchCriteria.languages,
405+
complexities: matchCriteria.complexity,
406+
categories: matchCriteria.category,
407+
languages: matchCriteria.language,
408408
timeout: matchCriteria.timeout,
409409
};
410410
matchSocket.emit(
@@ -430,9 +430,9 @@ const MatchProvider: React.FC<{ children?: React.ReactNode }> = (props) => {
430430
}
431431

432432
findMatch(
433-
matchCriteria.complexities,
434-
matchCriteria.categories,
435-
matchCriteria.languages,
433+
matchCriteria.complexity,
434+
matchCriteria.category,
435+
matchCriteria.language,
436436
matchCriteria.timeout
437437
);
438438
};

0 commit comments

Comments
 (0)