This commit is contained in:
MathieuSevignyLavallee 2024-11-28 15:09:22 -05:00
parent 71353669ca
commit ec15909d55
5 changed files with 104 additions and 70 deletions

View file

@ -9,60 +9,71 @@ export class RoomParticipant {
this.retryDelay = 1000;
}
async connectToRoom(baseUrl, onConnectCallback) {
async connectToRoom(baseUrl) {
let retries = 0;
const maxRetries = 2;
const retryDelay = 2000;
const connect = () => {
return new Promise((resolve, reject) => {
try {
const socket = io(baseUrl, {
path: `/api/room/${this.roomName}/socket`,
transports: ['websocket'],
timeout: 5000,
reconnection: true,
reconnectionAttempts: 3,
reconnectionDelay: 1000,
});
const cleanup = () => {
if (this.socket) {
this.socket.removeAllListeners();
this.socket.disconnect();
this.socket = null;
}
};
const connectionTimeout = setTimeout(() => {
socket.close();
while (retries < maxRetries) {
try {
const socket = io(baseUrl, {
path: `/api/room/${this.roomName}/socket`,
transports: ['websocket'],
timeout: 8000,
reconnection: false,
forceNew: true
});
const result = await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
cleanup();
reject(new Error('Connection timeout'));
}, 5000);
}, 8000);
socket.on('connect', () => {
clearTimeout(connectionTimeout);
clearTimeout(timeout);
this.socket = socket;
if (onConnectCallback) {
onConnectCallback();
}
this.onConnected(); // Add this line
resolve(socket);
});
socket.on('connect_error', (error) => {
clearTimeout(connectionTimeout);
clearTimeout(timeout);
cleanup();
reject(new Error(`Connection error: ${error.message}`));
});
} catch (error) {
reject(error);
}
});
};
socket.on('error', (error) => {
clearTimeout(timeout);
cleanup();
reject(new Error(`Socket error: ${error.message}`));
});
});
return result;
while (retries < this.maxRetries) {
try {
return await connect();
} catch (error) {
retries++;
if (retries === this.maxRetries) {
throw new Error(`Failed to connect ${this.username} after ${this.maxRetries} attempts: ${error.message}`);
if (retries === maxRetries) {
throw error;
}
console.warn(`Retry ${retries}/${this.maxRetries} for ${this.username}`);
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
}
onConnected() {
// To be implemented by child classes
}
disconnect() {
if (this.socket) {
this.socket.disconnect();

View file

@ -2,15 +2,20 @@
import { RoomParticipant } from './roomParticipant.js';
export class Student extends RoomParticipant {
nbrMessageReceived = 0;
constructor(username, roomName) {
super(username, roomName);
}
connectToRoom(baseUrl) {
return super.connectToRoom(baseUrl, () => {
this.joinRoom();
this.listenForTeacherMessage();
});
return super.connectToRoom(baseUrl);
}
onConnected() {
this.joinRoom();
this.listenForTeacherMessage();
}
joinRoom() {
@ -25,6 +30,7 @@ export class Student extends RoomParticipant {
listenForTeacherMessage() {
if (this.socket) {
this.socket.on('message-sent-teacher', ({ message }) => {
this.nbrMessageReceived++;
this.respondToTeacher(message);
});
}

View file

@ -1,22 +1,21 @@
import { RoomParticipant } from './roomParticipant.js';
export class Teacher extends RoomParticipant {
nbrMessageReceived = 0;
constructor(username, roomName) {
super(username, roomName);
this.ready = false;
}
connectToRoom(baseUrl) {
return super.connectToRoom(baseUrl, () => {
this.createRoom();
this.listenForStudentMessage();
// Add room creation confirmation listener
this.socket.on('create-success', () => {
console.log(`Room ${this.roomName} created by teacher ${this.username}`);
this.ready = true;
});
});
return super.connectToRoom(baseUrl);
}
onConnected() {
this.createRoom();
this.listenForStudentMessage();
}
createRoom() {
@ -26,7 +25,7 @@ export class Teacher extends RoomParticipant {
}
broadcastMessage(message) {
if (this.socket && this.ready) {
if (this.socket) {
this.socket.emit('message-from-teacher', {
roomName: this.roomName,
message
@ -40,6 +39,7 @@ export class Teacher extends RoomParticipant {
if (this.socket) {
this.socket.on('message-sent-student', ({ message }) => {
//console.log(`Teacher ${this.username} received: "${message}"`);
this.nbrMessageReceived++;
});
}
}

View file

@ -9,8 +9,11 @@ export class Watcher extends RoomParticipant {
super(username, roomName);
}
async connectToRoom(baseUrl) {
await super.connectToRoom(baseUrl);
connectToRoom(baseUrl) {
return super.connectToRoom(baseUrl);
}
onConnected() {
this.startCheckingResources();
}
@ -29,7 +32,7 @@ export class Watcher extends RoomParticipant {
}
}
startCheckingResources(intervalMs = 500) {
startCheckingResources(intervalMs = 250) {
if (this.checkRessourceInterval) {
console.warn(`Resource checking is already running for room ${this.roomName}.`);
return;

View file

@ -7,18 +7,19 @@ import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
const BASE_URL = process.env.BASE_URL || 'http://localhost';
const user = {
username: process.env.USER_EMAIL || 'admin@admin.com',
password: process.env.USER_PASSWORD || 'admin'
const BASE_URL = process.env.BASE_URL || 'http://msevignyl.duckdns.org';
const user = {
username: process.env.USER_EMAIL || 'admin@admin.com',
password: process.env.USER_PASSWORD || 'admin'
};
const numberRooms = parseInt(process.env.NUMBER_ROOMS || '5');
const numberRooms = parseInt(process.env.NUMBER_ROOMS || '50');
const usersPerRoom = parseInt(process.env.USERS_PER_ROOM || '60');
const roomAssociations = {};
const maxMessages = parseInt(process.env.MAX_MESSAGES || '20');
const conversationInterval = parseInt(process.env.CONVERSATION_INTERVAL || '1000');
const batchSize = 10; // Number of simultaneous connections
const batchSize = 5;
const batchDelay = 500;
const roomDelay = 1000;
/**
* Creates a room and immediately connects a teacher to it.
@ -30,8 +31,6 @@ async function createRoomWithTeacher(token, index) {
throw new Error('Room creation failed');
}
console.log(`Room ${index + 1} created with ID: ${room.id}`);
// Initialize room associations
roomAssociations[room.id] = { watcher: null, teacher: null, students: [] };
@ -41,7 +40,6 @@ async function createRoomWithTeacher(token, index) {
// Connect teacher to room
await teacher.connectToRoom(BASE_URL);
console.log(`Teacher connected to room ${room.id}`);
return room.id;
} catch (err) {
@ -79,7 +77,6 @@ function addRemainingUsers() {
const participants = roomAssociations[roomId];
// Add watcher
console.log('Adding users to room ' + roomId);
participants.watcher = new Watcher(`watcher_${roomIndex}`, roomId);
// Add students
@ -95,32 +92,49 @@ function addRemainingUsers() {
*/
async function connectRemainingParticipants(baseUrl) {
console.log('Connecting remaining participants in batches');
for (const [roomId, participants] of Object.entries(roomAssociations)) {
console.log(`Processing room ${roomId}`);
// Collect remaining participants for this room
const remainingParticipants = [
participants.watcher,
...participants.students
].filter(Boolean);
// Process participants in batches
// Connect in smaller batches with longer delays
for (let i = 0; i < remainingParticipants.length; i += batchSize) {
const batch = remainingParticipants.slice(i, i + batchSize);
// Add connection timeout handling
const batchPromises = batch.map(participant =>
participant.connectToRoom(baseUrl)
.catch(err => {
console.warn(
`Failed to connect ${participant.username} in room ${roomId}:`,
err.message
);
return null;
})
Promise.race([
participant.connectToRoom(baseUrl),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Connection timeout')), 10000)
)
]).catch(err => {
console.warn(
`Failed to connect ${participant.username} in room ${roomId}:`,
err.message
);
return null;
})
);
await Promise.all(batchPromises);
// Cleanup disconnected sockets
batch.forEach(participant => {
if (!participant.socket?.connected) {
participant.disconnect();
}
});
await new Promise(resolve => setTimeout(resolve, batchDelay));
}
// Add delay between rooms
await new Promise(resolve => setTimeout(resolve, roomDelay));
}
console.log('Finished connecting remaining participants');