backend: Refactored room GC using bulk delete rooom method for gracefully delete the expired rooms

This commit is contained in:
Carlos Santos 2025-04-09 12:22:27 +02:00
parent 7ca385968d
commit 913aa44278

View File

@ -159,11 +159,13 @@ export class RoomService {
} }
/** /**
* Deletes a room by its ID. * Deletes multiple rooms in bulk, with the option to force delete or gracefully handle rooms with active participants.
* For rooms with participants, when `forceDelete` is false, the method performs a "graceful deletion"
* by marking the room as deleted without disrupting active sessions.
* *
* @param roomId - The unique identifier of the room to delete. * @param roomIds - Array of room identifiers to be deleted
* @param forceDelete - Whether to force delete the room even if it has participants. * @param forceDelete - If true, deletes rooms even if they have active participants.
* @returns A promise that resolves to an object containing the deleted and marked rooms. * If false, rooms with participants will be marked for deletion instead of being deleted immediately.
*/ */
async bulkDeleteRooms( async bulkDeleteRooms(
roomIds: string[], roomIds: string[],
@ -187,10 +189,7 @@ export class RoomService {
} }
this.logger.verbose(`Room ${roomId} has participants. Marking as deleted (graceful deletion).`); this.logger.verbose(`Room ${roomId} has participants. Marking as deleted (graceful deletion).`);
// Mark room as deleted await this.markRoomAsDeleted(roomId);
const room = await this.storageService.getMeetRoom(roomId);
room.markedForDeletion = true;
await this.storageService.saveMeetRoom(room);
return { roomId, status: 'marked' } as const; return { roomId, status: 'marked' } as const;
}) })
); );
@ -222,6 +221,19 @@ export class RoomService {
} }
} }
/**
* Marks a room as deleted in the storage system.
*
* @param roomId - The unique identifier of the room to mark for deletion
* @returns A promise that resolves when the room has been successfully marked as deleted
* @throws May throw an error if the room cannot be found or if saving fails
*/
protected async markRoomAsDeleted(roomId: string): Promise<void> {
const room = await this.storageService.getMeetRoom(roomId);
room.markedForDeletion = true;
await this.storageService.saveMeetRoom(room);
}
/** /**
* Validates a secret against a room's moderator and publisher secrets and returns the corresponding role. * Validates a secret against a room's moderator and publisher secrets and returns the corresponding role.
* *
@ -290,52 +302,21 @@ export class RoomService {
} }
/** /**
* Deletes OpenVidu expired rooms and consequently LiveKit rooms. * Gracefully deletes expired rooms.
* *
* This method delete the rooms that have an expiration date earlier than the current time. * This method checks for rooms that have an auto-deletion date in the past and deletes them.
* * It also marks rooms as deleted if they have participants.
* @returns {Promise<void>} A promise that resolves when the deletion process is complete.
**/
protected async deleteExpiredRooms(): Promise<void> {
try {
const ovExpiredRooms = await this.deleteExpiredMeetRooms();
if (ovExpiredRooms.length === 0) return;
const livekitResults = await Promise.allSettled(
ovExpiredRooms.map((roomId) => this.livekitService.deleteRoom(roomId))
);
const successfulRooms: string[] = [];
livekitResults.forEach((result, index) => {
if (result.status === 'fulfilled') {
successfulRooms.push(ovExpiredRooms[index]);
} else {
this.logger.error(`Failed to delete OpenVidu room "${ovExpiredRooms[index]}": ${result.reason}`);
}
});
this.logger.verbose(
`Successfully deleted ${successfulRooms.length} expired rooms: ${successfulRooms.join(', ')}`
);
} catch (error) {
this.logger.error('Error deleting expired rooms:', error);
}
}
/**
* Deletes expired Meet rooms by iterating through all paged results.
*
* @returns A promise that resolves with an array of room IDs that were successfully deleted.
*/ */
protected async deleteExpiredMeetRooms(): Promise<string[]> { protected async deleteExpiredRooms(): Promise<void> {
const now = Date.now();
this.logger.verbose(`Checking Meet expired rooms at ${new Date(now).toISOString()}`);
let nextPageToken: string | undefined; let nextPageToken: string | undefined;
const deletedRooms: string[] = []; const deletedRooms: string[] = [];
const markedAsDeletedRooms: string[] = [];
this.logger.verbose(`Checking expired rooms at ${new Date(Date.now()).toISOString()}`);
try {
do { do {
const now = Date.now();
const { rooms, nextPageToken: token } = await this.getAllMeetRooms({ maxItems: 100, nextPageToken }); const { rooms, nextPageToken: token } = await this.getAllMeetRooms({ maxItems: 100, nextPageToken });
nextPageToken = token; nextPageToken = token;
@ -345,14 +326,20 @@ export class RoomService {
if (expiredRoomIds.length > 0) { if (expiredRoomIds.length > 0) {
this.logger.verbose( this.logger.verbose(
`Deleting ${expiredRoomIds.length} expired Meet rooms: ${expiredRoomIds.join(', ')}` `Trying to delete ${expiredRoomIds.length} expired Meet rooms: ${expiredRoomIds.join(', ')}`
); );
// const deletedOnPage = await this.deleteMeetRooms(expiredRooms);
await this.storageService.deleteMeetRooms(expiredRoomIds); const { deleted, markedAsDeleted } = await this.bulkDeleteRooms(expiredRoomIds, false);
deletedRooms.push(...expiredRoomIds);
deletedRooms.push(...deleted);
markedAsDeletedRooms.push(...markedAsDeleted);
} }
} while (nextPageToken); } while (nextPageToken);
return deletedRooms; this.logger.verbose(`Successfully deleted ${deletedRooms.length} expired rooms}`);
this.logger.verbose(`Marked as deleted ${markedAsDeletedRooms.length} expired rooms}`);
} catch (error) {
this.logger.error('Error deleting expired rooms:', error);
}
} }
} }