backend: Rename handleRoomFinished to handleMeetingFinished and improve recording lock management

This commit is contained in:
Carlos Santos 2025-03-24 11:21:08 +01:00
parent f089ad6e67
commit da5d513d01
3 changed files with 15 additions and 8 deletions

View File

@ -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;

View File

@ -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}`);
}

View File

@ -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<RedisLock | null> {
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}`);