Skip to content

Commit b0f62eb

Browse files
committed
Add Cancel Match logic
1 parent 7e85576 commit b0f62eb

File tree

6 files changed

+112
-15
lines changed

6 files changed

+112
-15
lines changed

Backend/MatchingService/rabbitmq/publisher.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,26 @@ async function publishToQueue({userId, difficulty, language}) {
3838
}
3939
}
4040

41-
module.exports = { publishToQueue };
41+
async function publishCancelRequest({ userId }) {
42+
try {
43+
const channel = await connectToRabbitMQ(); // Reuse persistent connection
44+
const routingKey = 'cancel'; // Define a routing key for cancellation
45+
46+
// Publish the cancel message to the exchange
47+
const messageSent = channel.publish(
48+
matching_exchange_name,
49+
routingKey,
50+
Buffer.from(JSON.stringify({ userId }))
51+
);
52+
53+
if (messageSent) {
54+
console.log(`Cancel request sent: ${userId}`);
55+
} else {
56+
console.error(`Cancel request NOT sent: ${userId}`);
57+
}
58+
} catch (error) {
59+
console.error('Error publishing cancel request to RabbitMQ:', error);
60+
}
61+
}
62+
63+
module.exports = { publishToQueue, publishCancelRequest };

Backend/MatchingService/rabbitmq/setup.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const amqp = require("amqplib");
33
const matching_exchange_name = "matching_exchange";
44
const dead_letter_exchange_name = "dead_letter_exchange";
55
const dead_letter_queue_name = "dead_letter_queue";
6+
const cancel_queue_name = "cancel_queue";
67
const queueNames = [
78
'easy.python',
89
'easy.java',
@@ -53,6 +54,11 @@ async function setupRabbitMQ() {
5354
await channel.assertQueue(dead_letter_queue_name, { durable: false });
5455
await channel.bindQueue(dead_letter_queue_name, dead_letter_exchange_name, ''); // Bind with no routing key
5556

57+
// Declare and bind the cancel queue
58+
await channel.deleteQueue(cancel_queue_name); // Delete any existing cancel queue
59+
await channel.assertQueue(cancel_queue_name, { durable: false }); // Declare the cancel queue
60+
await channel.bindQueue(cancel_queue_name, matching_exchange_name, 'cancel'); // Bind with the "cancel" routing key
61+
5662
console.log("RabbitMQ setup complete with queues, DLQ, and bindings.");
5763

5864
await channel.close();
@@ -62,4 +68,4 @@ async function setupRabbitMQ() {
6268
}
6369
}
6470

65-
module.exports = { setupRabbitMQ, matching_exchange_name, queueNames, dead_letter_queue_name };
71+
module.exports = { setupRabbitMQ, matching_exchange_name, queueNames, dead_letter_queue_name , cancel_queue_name};

Backend/MatchingService/rabbitmq/subscriber.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ async function consumeQueue() {
140140
}
141141

142142
console.log("Listening to matchmaking queues");
143+
144+
await consumeCancelQueue();
145+
console.log("Listening to Cancel Queue")
143146
} catch (error) {
144147
console.error('Error consuming RabbitMQ queue:', error);
145148
}
@@ -172,4 +175,54 @@ async function consumeDLQ() {
172175
}
173176
}
174177

178+
async function consumeCancelQueue() {
179+
try {
180+
const connection = await amqp.connect(process.env.RABBITMQ_URL);
181+
const channel = await connection.createChannel();
182+
183+
// Subscribe to the cancel queue
184+
await channel.consume('cancel_queue', async (msg) => {
185+
if (msg !== null) {
186+
const { userId } = JSON.parse(msg.content.toString());
187+
188+
console.log(`Received cancel request for user: ${userId}`);
189+
190+
// Process the cancel request
191+
await cancelMatching(channel, msg, userId);
192+
}
193+
});
194+
195+
console.log("Listening for cancel requests");
196+
} catch (error) {
197+
console.error('Error consuming cancel queue:', error);
198+
}
199+
}
200+
201+
async function cancelMatching(channel, msg, userId) {
202+
try {
203+
// Loop through waitingUsers to find the user
204+
Object.keys(waitingUsers).forEach(criteriaKey => {
205+
const userIndex = waitingUsers[criteriaKey].findIndex(user => user.userId === userId);
206+
207+
if (userIndex !== -1) {
208+
waitingUsers[criteriaKey].splice(userIndex, 1);
209+
console.log(`User ${userId} removed from waiting list for ${criteriaKey}`);
210+
}
211+
});
212+
213+
// Clean up the timeout
214+
if (timeoutMap[userId]) {
215+
clearTimeout(timeoutMap[userId]);
216+
delete timeoutMap[userId];
217+
}
218+
219+
// Acknowledge the cancel message
220+
channel.ack(msg);
221+
222+
console.log(`Cancel processed for user ${userId}`);
223+
} catch (error) {
224+
console.error(`Failed to process cancel for user ${userId}:`, error);
225+
}
226+
}
227+
175228
module.exports = { consumeQueue, consumeDLQ };

Backend/MatchingService/websocket/websocket.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { publishToQueue } = require('../rabbitmq/publisher');
1+
const { publishToQueue , publishCancelRequest} = require('../rabbitmq/publisher');
22
const WebSocket = require('ws');
33
const wss = new WebSocket.Server({ port: 8080 });
44

@@ -11,17 +11,28 @@ wss.on('connection', (ws) => {
1111
console.log(`Received message: ${message}`);
1212

1313
// Parse the message to extract userId, difficulty, and language
14-
const { userId, difficulty, language } = JSON.parse(message);
14+
const { userId, difficulty, language , action} = JSON.parse(message);
1515

1616
// Store userId in WebSocket connection
1717
ws.userId = userId;
1818

19-
// Call the RabbitMQ publisher to publish this message to the queue
20-
await publishToQueue({ userId, difficulty, language });
21-
console.log('Message published to RabbitMQ');
19+
if (action === 'match') {
20+
// Call the RabbitMQ publisher to publish this message to the queue
21+
await publishToQueue({ userId, difficulty, language });
22+
console.log('Message published to RabbitMQ');
23+
24+
// Notify the user that their message has been processed successfully
25+
ws.send(JSON.stringify({ status: 'su9ccess', message: 'Match request sent!' }));
26+
} else if (action === 'cancel') {
27+
await publishCancelRequest({ userId });
28+
console.log('Cancel request published to RabbitMQ');
29+
30+
// Notify the user that their cancel request has been processed successfully
31+
ws.send(JSON.stringify({ status: 'success', message: 'Match request cancelled!' }));
32+
}
33+
34+
2235

23-
// Notify the user that their message has been processed successfully
24-
ws.send(JSON.stringify({ status: 'success', message: 'Match request sent!' }));
2536
} catch (error) {
2637
console.error('Error handling WebSocket message:', error);
2738
ws.send(JSON.stringify({ status: 'error', message: 'Match request failed!' }));

Frontend/src/components/Sidebar.jsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function Sidebar() {
4242

4343
websocket.onmessage = (event) => {
4444
const { message, type } = JSON.parse(event.data);
45-
console.log(`Notification from server: ${message} ////////////////////////////`);
45+
console.log(`Notification from server: ${message}`);
4646

4747
if (type === 'match') {
4848
setShowMatching(false);
@@ -62,14 +62,19 @@ function Sidebar() {
6262

6363
const handleMatch = () => {
6464
if (ws && difficulty && language) {
65-
setShowMatching(true);
65+
handleShowMatching();
6666
setShowUnsuccessfulMatch(false);
67-
ws.send(JSON.stringify({ userId, difficulty, language })); // Send to server
67+
ws.send(JSON.stringify({ userId, difficulty, language , action: 'match'})); // Send to server
6868
} else {
6969
alert('Please select a difficulty and language.');
7070
}
7171
};
7272

73+
const handleCancel = () => {
74+
handleCloseMatching();
75+
ws.send(JSON.stringify({ userId, action: 'cancel' }));
76+
};
77+
7378
return (
7479
<Stack gap={3} className='p-3 m-3 justify-content-center align-items-center'>
7580
<Card>
@@ -104,7 +109,7 @@ function Sidebar() {
104109

105110
{/* Modals */}
106111
<Modal show={showMatching} onHide={handleCloseMatching} backdrop="static" className="custom-modal" centered>
107-
<Matching handleClose={handleCloseMatching}/>
112+
<Matching handleCancel={handleCancel}/>
108113
</Modal>
109114

110115
<Modal show={showSuccessfulMatch} onHide={handleCloseSuccessfulMatch} backdrop="static" className="custom-modal" centered>

Frontend/src/components/matching/Matching.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
22
import CloseButton from 'react-bootstrap/esm/CloseButton';
33
import '../../css/Matching.css';
44

5-
function Matching({ handleClose }) {
5+
function Matching({ handleCancel }) {
66
const [seconds, setSeconds] = useState(0);
77
const [minutes, setMinutes] = useState(0);
88

@@ -26,7 +26,7 @@ function Matching({ handleClose }) {
2626
<div className="matching-text">
2727
<h2>Matching you....</h2>
2828
<p>{`${minutes < 10 ? `0${minutes}` : minutes}:${seconds < 10 ? `0${seconds}` : seconds}`}</p>
29-
{/*<button className='btn btn-primary btn-sm' onClick={handleClose} >Cancel</button>*/}
29+
<button className='btn btn-primary btn-sm' onClick={handleCancel} >Cancel</button>
3030
</div>
3131
</div>
3232
);

0 commit comments

Comments
 (0)