From da5d513d0196f00485788202799ee4c548990ccb Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Mon, 24 Mar 2025 11:21:08 +0100 Subject: [PATCH] backend: Rename handleRoomFinished to handleMeetingFinished and improve recording lock management --- .../controllers/livekit-webhook.controller.ts | 2 +- backend/src/services/livekit-webhook.service.ts | 5 ++++- backend/src/services/recording.service.ts | 16 ++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/backend/src/controllers/livekit-webhook.controller.ts b/backend/src/controllers/livekit-webhook.controller.ts index daaba55..ca58adb 100644 --- a/backend/src/controllers/livekit-webhook.controller.ts +++ b/backend/src/controllers/livekit-webhook.controller.ts @@ -39,7 +39,7 @@ export const lkWebhookHandler = async (req: Request, res: Response) => { await lkWebhookService.handleParticipantJoined(room!, participant!); break; case 'room_finished': - await lkWebhookService.handleRoomFinished(room!); + await lkWebhookService.handleMeetingFinished(room!); break; default: break; diff --git a/backend/src/services/livekit-webhook.service.ts b/backend/src/services/livekit-webhook.service.ts index 6cfa4c1..bc8c80c 100644 --- a/backend/src/services/livekit-webhook.service.ts +++ b/backend/src/services/livekit-webhook.service.ts @@ -127,7 +127,10 @@ export class LivekitWebhookService { */ async handleMeetingFinished(room: Room) { try { - await this.openViduWebhookService.sendRoomFinishedWebhook(room); + await Promise.all([ + this.recordingService.releaseRoomRecordingActiveLock(room.name), + this.openViduWebhookService.sendRoomFinishedWebhook(room) + ]); } catch (error) { this.logger.error(`Error handling room finished event: ${error}`); } diff --git a/backend/src/services/recording.service.ts b/backend/src/services/recording.service.ts index 5e186e8..06f3952 100644 --- a/backend/src/services/recording.service.ts +++ b/backend/src/services/recording.service.ts @@ -26,7 +26,6 @@ import { OpenViduComponentsAdapterHelper } from '../helpers/ov-components-adapte @injectable() export class RecordingService { - protected readonly RECORDING_ACTIVE_LOCK_TTL = ms('6h'); constructor( @inject(S3Service) protected s3Service: S3Service, @inject(LiveKitService) protected livekitService: LiveKitService, @@ -43,8 +42,7 @@ export class RecordingService { if (!room) throw errorRoomNotFound(roomId); - // Attempt to acquire lock. - // Note: using a high TTL to prevent expiration during a long recording. + // Attempt to acquire lock. If the lock is not acquired, the recording is already active. acquiredLock = await this.acquireRoomRecordingActiveLock(roomId); if (!acquiredLock) throw errorRecordingAlreadyStarted(roomId); @@ -53,8 +51,6 @@ export class RecordingService { const output = this.generateFileOutputFromRequest(roomId); const egressInfo = await this.livekitService.startRoomComposite(roomId, output, options); - // Return recording info without releasing the lock here, - // as it will be released in handleEgressEnded on successful completion. return RecordingHelper.toRecordingInfo(egressInfo); } catch (error) { this.logger.error(`Error starting recording in room ${roomId}: ${error}`); @@ -270,12 +266,20 @@ export class RecordingService { /** * Acquires a Redis-based lock to indicate that a recording is active for a specific room. + * + * This lock will be used to prevent multiple recording start requests from being processed + * simultaneously for the same room. + * + * The active recording lock will be released when the recording ends (handleEgressEnded) or when the room is finished (handleMeetingFinished). + * + * @param roomName - The name of the room to acquire the lock for. */ async acquireRoomRecordingActiveLock(roomName: string): Promise { const lockName = `${roomName}_${RedisLockName.RECORDING_ACTIVE}`; + const LOCK_TTL = ms('6h'); try { - const lock = await this.mutexService.acquire(lockName, this.RECORDING_ACTIVE_LOCK_TTL); + const lock = await this.mutexService.acquire(lockName, LOCK_TTL); return lock; } catch (error) { this.logger.warn(`Error acquiring lock ${lockName} on egress started: ${error}`);