backend: Enhance MutexService with lock registry management and retrieval methods
This commit is contained in:
parent
318fecf962
commit
049ff37c58
@ -1,7 +1,7 @@
|
|||||||
import Redlock, { Lock } from 'redlock';
|
|
||||||
import { RedisService } from './redis.service.js';
|
|
||||||
import { inject, injectable } from 'inversify';
|
|
||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
|
import Redlock, { Lock } from 'redlock';
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { RedisService } from './redis.service.js';
|
||||||
import { LoggerService } from './logger.service.js';
|
import { LoggerService } from './logger.service.js';
|
||||||
|
|
||||||
export type RedisLock = Lock;
|
export type RedisLock = Lock;
|
||||||
@ -11,6 +11,7 @@ export class MutexService {
|
|||||||
protected locks: Map<string, Lock>;
|
protected locks: Map<string, Lock>;
|
||||||
protected readonly TTL_MS = ms('1m');
|
protected readonly TTL_MS = ms('1m');
|
||||||
protected LOCK_KEY_PREFIX = 'ov_meet_lock:';
|
protected LOCK_KEY_PREFIX = 'ov_meet_lock:';
|
||||||
|
protected LOCK_REGISTRY_PREFIX = 'ov_meet_lock_registry:';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@inject(RedisService) protected redisService: RedisService,
|
@inject(RedisService) protected redisService: RedisService,
|
||||||
@ -28,12 +29,25 @@ export class MutexService {
|
|||||||
* @returns A Promise that resolves to the acquired Lock object.
|
* @returns A Promise that resolves to the acquired Lock object.
|
||||||
*/
|
*/
|
||||||
async acquire(resource: string, ttl: number = this.TTL_MS): Promise<Lock | null> {
|
async acquire(resource: string, ttl: number = this.TTL_MS): Promise<Lock | null> {
|
||||||
const key = this.LOCK_KEY_PREFIX + resource;
|
const key = this.getLockKey(resource);
|
||||||
|
const registryKey = this.getLockRegistryKey(resource);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug(`Acquiring lock for resource: ${resource}`);
|
this.logger.debug(`Acquiring lock for resource: ${resource}`);
|
||||||
const lock = await this.redlockWithoutRetry.acquire([key], ttl);
|
const lock = await this.redlockWithoutRetry.acquire([key], ttl);
|
||||||
|
|
||||||
|
// Store the lock in memory for easy access (optional)
|
||||||
this.locks.set(key, lock);
|
this.locks.set(key, lock);
|
||||||
|
// Store Lock metadata in Redis registry for support HA release
|
||||||
|
await this.redisService.set(
|
||||||
|
registryKey,
|
||||||
|
JSON.stringify({
|
||||||
|
resources: lock.resources,
|
||||||
|
value: lock.value,
|
||||||
|
expiration: lock.expiration
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
return lock;
|
return lock;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Error acquiring lock:', error);
|
this.logger.error('Error acquiring lock:', error);
|
||||||
@ -48,8 +62,13 @@ export class MutexService {
|
|||||||
* @returns A Promise that resolves when the lock is released.
|
* @returns A Promise that resolves when the lock is released.
|
||||||
*/
|
*/
|
||||||
async release(resource: string): Promise<void> {
|
async release(resource: string): Promise<void> {
|
||||||
const key = this.LOCK_KEY_PREFIX + resource;
|
const key = this.getLockKey(resource);
|
||||||
const lock = this.locks.get(key);
|
|
||||||
|
const lock = await this.getLockData(resource);
|
||||||
|
|
||||||
|
if (!lock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (lock) {
|
if (lock) {
|
||||||
this.logger.debug(`Releasing lock for resource: ${resource}`);
|
this.logger.debug(`Releasing lock for resource: ${resource}`);
|
||||||
@ -60,7 +79,64 @@ export class MutexService {
|
|||||||
this.logger.error(`Error releasing lock for key ${key}:`, error);
|
this.logger.error(`Error releasing lock for key ${key}:`, error);
|
||||||
} finally {
|
} finally {
|
||||||
this.locks.delete(key);
|
this.locks.delete(key);
|
||||||
|
await this.redisService.delete(this.getLockRegistryKey(resource));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the complete key used to acquire the lock in Redis.
|
||||||
|
*/
|
||||||
|
protected getLockKey(resource: string): string {
|
||||||
|
return `${this.LOCK_KEY_PREFIX}${resource}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a unique key for the lock registry by combining a predefined prefix
|
||||||
|
* with the specified resource identifier.
|
||||||
|
*/
|
||||||
|
protected getLockRegistryKey(resource: string): string {
|
||||||
|
return `${this.LOCK_REGISTRY_PREFIX}${resource}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the lock data for a given resource.
|
||||||
|
*
|
||||||
|
* This method first attempts to retrieve the lock from memory. If the lock is not found in memory,
|
||||||
|
* it then tries to fetch the lock data from Redis. If the lock data is successfully retrieved from Redis,
|
||||||
|
* it constructs a new `Lock` instance and returns it. If the lock data cannot be found in either memory
|
||||||
|
* or Redis, the method returns `null`.
|
||||||
|
*
|
||||||
|
* @param resource - The identifier of the resource for which the lock data is being retrieved.
|
||||||
|
* @returns A promise that resolves to the `Lock` instance if found, or `null` if the lock data is not available.
|
||||||
|
*/
|
||||||
|
protected async getLockData(resource: string): Promise<Lock | null> {
|
||||||
|
const key = this.getLockKey(resource);
|
||||||
|
const registryKey = this.getLockRegistryKey(resource);
|
||||||
|
|
||||||
|
// Try to get lock from memory
|
||||||
|
let lock = this.locks.get(key);
|
||||||
|
|
||||||
|
if (lock) {
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.logger.debug(`Getting lock data in Redis for resource: ${resource}`);
|
||||||
|
// Try to get lock from Redis
|
||||||
|
const redisLockData = await this.redisService.get(registryKey);
|
||||||
|
|
||||||
|
if (!redisLockData) {
|
||||||
|
this.logger.error(`Cannot release lock. Lock not found for resource: ${resource}.`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { resources, value, expiration } = JSON.parse(redisLockData);
|
||||||
|
lock = new Lock(this.redlockWithoutRetry, resources, value, [], expiration);
|
||||||
|
return lock;
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`Cannot release lock. Lock not found for resource: ${resource}.`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user