diff --git a/backend/src/config/dependency-injector.config.ts b/backend/src/config/dependency-injector.config.ts index 6cd692d..76809e3 100644 --- a/backend/src/config/dependency-injector.config.ts +++ b/backend/src/config/dependency-injector.config.ts @@ -57,7 +57,7 @@ export const registerDependencies = () => { export const initializeEagerServices = async () => { // Force the creation of services that need to be initialized at startup container.get(RecordingService); - await container.get(MeetStorageService).buildAndSaveDefaultPreferences(); + await container.get(MeetStorageService).initializeGlobalPreferences(); }; export { injectable, inject } from 'inversify'; diff --git a/backend/src/helpers/redis.helper.ts b/backend/src/helpers/redis.helper.ts index f5fc203..fc8fd8e 100644 --- a/backend/src/helpers/redis.helper.ts +++ b/backend/src/helpers/redis.helper.ts @@ -32,4 +32,8 @@ export class MeetLock { static getRoomGarbageCollectorLock(): string { return `${RedisLockPrefix.BASE}${RedisLockName.ROOM_GARBAGE_COLLECTOR}`; } + + static getGlobalPreferencesLock(): string { + return `${RedisLockPrefix.BASE}${RedisLockName.GLOBAL_PREFERENCES}`; + } } diff --git a/backend/src/models/redis.model.ts b/backend/src/models/redis.model.ts index 94e2ff4..c26720e 100644 --- a/backend/src/models/redis.model.ts +++ b/backend/src/models/redis.model.ts @@ -15,5 +15,6 @@ export const enum RedisLockPrefix { export const enum RedisLockName { ROOM_GARBAGE_COLLECTOR = 'room_garbage_collector', RECORDING_ACTIVE = 'recording_active', - SCHEDULED_TASK = 'scheduled_task' + SCHEDULED_TASK = 'scheduled_task', + GLOBAL_PREFERENCES = 'global_preferences', } diff --git a/backend/src/services/mutex.service.ts b/backend/src/services/mutex.service.ts index ba9be84..14359ea 100644 --- a/backend/src/services/mutex.service.ts +++ b/backend/src/services/mutex.service.ts @@ -45,7 +45,7 @@ export class MutexService { ); return lock; } catch (error) { - this.logger.error('Error acquiring lock:', error); + this.logger.warn('Error acquiring lock:', error); return null; } } diff --git a/backend/src/services/storage/storage.service.ts b/backend/src/services/storage/storage.service.ts index e040b39..fa29ff7 100644 --- a/backend/src/services/storage/storage.service.ts +++ b/backend/src/services/storage/storage.service.ts @@ -6,6 +6,9 @@ import { errorRoomNotFound, OpenViduMeetError } from '../../models/error.model.j import { MEET_NAME_ID, MEET_SECRET, MEET_USER, MEET_WEBHOOK_ENABLED, MEET_WEBHOOK_URL } from '../../environment.js'; import { injectable, inject } from '../../config/dependency-injector.config.js'; import { PasswordHelper } from '../../helpers/password.helper.js'; +import { MutexService } from '../mutex.service.js'; +import { MeetLock } from '../../helpers/redis.helper.js'; +import ms from 'ms'; /** * A service for managing storage operations related to OpenVidu Meet rooms and preferences. @@ -21,7 +24,8 @@ export class MeetStorageService} Default global preferences. */ - async buildAndSaveDefaultPreferences(): Promise { - const preferences = await this.getDefaultPreferences(); - + async initializeGlobalPreferences(): Promise { try { + // Acquire a global lock to prevent multiple initializations at the same time when running in HA mode + const lock = await this.mutexService.acquire(MeetLock.getGlobalPreferencesLock(), ms('30s')); + + if (!lock) { + this.logger.warn( + 'Unable to acquire lock for global preferences initialization. May be already initialized by another instance.' + ); + return; + } + + const preferences = await this.getDefaultPreferences(); + this.logger.verbose('Initializing global preferences with default values'); await this.storageProvider.initialize(preferences); - return preferences as G; } catch (error) { this.handleError(error, 'Error initializing default preferences'); - return Promise.resolve({} as G); } } @@ -52,7 +64,9 @@ export class MeetStorageService; } /**