add try lock
This commit is contained in:
@@ -13,9 +13,11 @@ import { usePagination, validateCronExpression } from '../domain.util';
|
||||
import { IBaseJob, IEntityJob, ILibraryFileJob, ILibraryRefreshJob, JOBS_ASSET_PAGINATION_SIZE, JobName } from '../job';
|
||||
|
||||
import {
|
||||
DatabaseLock,
|
||||
IAccessRepository,
|
||||
IAssetRepository,
|
||||
ICryptoRepository,
|
||||
IDatabaseRepository,
|
||||
IJobRepository,
|
||||
ILibraryRepository,
|
||||
IStorageRepository,
|
||||
@@ -53,6 +55,7 @@ export class LibraryService extends EventEmitter {
|
||||
@Inject(ILibraryRepository) private repository: ILibraryRepository,
|
||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||
@Inject(IUserRepository) private userRepository: IUserRepository,
|
||||
@Inject(IDatabaseRepository) private databaseRepository: IDatabaseRepository,
|
||||
) {
|
||||
super();
|
||||
this.access = AccessCore.create(accessRepository);
|
||||
@@ -103,6 +106,7 @@ export class LibraryService extends EventEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.databaseRepository.withTryLock(DatabaseLock.LibraryWatch, async () => {
|
||||
await this.unwatch(id);
|
||||
|
||||
this.logger.log(`Starting to watch library ${library.id} with import path(s) ${library.importPaths}`);
|
||||
@@ -155,6 +159,7 @@ export class LibraryService extends EventEmitter {
|
||||
|
||||
// Wait for the watcher to initialize before returning
|
||||
await ready$;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export enum DatabaseLock {
|
||||
Migrations = 200,
|
||||
StorageTemplateMigration = 420,
|
||||
CLIPDimSize = 512,
|
||||
LibraryWatch = 1337,
|
||||
}
|
||||
|
||||
export const extName: Record<DatabaseExtension, string> = {
|
||||
@@ -46,6 +47,7 @@ export interface IDatabaseRepository {
|
||||
shouldReindex(name: VectorIndex): Promise<boolean>;
|
||||
runMigrations(options?: { transaction?: 'all' | 'none' | 'each' }): Promise<void>;
|
||||
withLock<R>(lock: DatabaseLock, callback: () => Promise<R>): Promise<R>;
|
||||
withTryLock<R>(lock: DatabaseLock, callback: () => Promise<R>): Promise<R>;
|
||||
isBusy(lock: DatabaseLock): boolean;
|
||||
wait(lock: DatabaseLock): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -210,6 +210,25 @@ export class DatabaseRepository implements IDatabaseRepository {
|
||||
return res as R;
|
||||
}
|
||||
|
||||
async withTryLock<R>(lock: DatabaseLock, callback: () => Promise<R>): Promise<R> {
|
||||
let res;
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
try {
|
||||
const lockAcquired = await this.tryLock(lock, queryRunner);
|
||||
if (lockAcquired) {
|
||||
res = await callback();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
await this.releaseLock(lock, queryRunner);
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
return res as R;
|
||||
}
|
||||
|
||||
isBusy(lock: DatabaseLock): boolean {
|
||||
return this.asyncLock.isBusy(DatabaseLock[lock]);
|
||||
}
|
||||
@@ -222,6 +241,10 @@ export class DatabaseRepository implements IDatabaseRepository {
|
||||
return queryRunner.query('SELECT pg_advisory_lock($1)', [lock]);
|
||||
}
|
||||
|
||||
private async tryLock(lock: DatabaseLock, queryRunner: QueryRunner): Promise<boolean> {
|
||||
return queryRunner.query('SELECT pg_try_advisory_lock($1)', [lock]);
|
||||
}
|
||||
|
||||
private async releaseLock(lock: DatabaseLock, queryRunner: QueryRunner): Promise<void> {
|
||||
return queryRunner.query('SELECT pg_advisory_unlock($1)', [lock]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user