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