From 00ec2bd52507099bc76a6d18f6f19bbd8dbb81ac Mon Sep 17 00:00:00 2001 From: Jonathan Jogenfors Date: Fri, 7 Feb 2025 23:19:41 +0100 Subject: [PATCH] feat: add job ids --- server/src/interfaces/job.interface.ts | 1 - server/src/repositories/job.repository.ts | 16 ++++++++++++++++ server/src/services/library.service.spec.ts | 13 +------------ server/src/services/library.service.ts | 11 ++++++++--- 4 files changed, 25 insertions(+), 16 deletions(-) diff --git a/server/src/interfaces/job.interface.ts b/server/src/interfaces/job.interface.ts index 1f2b92074a..beaba82d45 100644 --- a/server/src/interfaces/job.interface.ts +++ b/server/src/interfaces/job.interface.ts @@ -144,7 +144,6 @@ export interface IAssetDeleteJob extends IEntityJob { } export interface ILibraryFileJob extends IEntityJob { - ownerId: string; assetPath: string; } diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index 9a5bf20df6..d8d5af2119 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -216,6 +216,17 @@ export class JobRepository implements IJobRepository { private getJobOptions(item: JobItem): JobsOptions | null { switch (item.name) { + case JobName.LIBRARY_QUEUE_SYNC_ASSETS: + case JobName.LIBRARY_QUEUE_SYNC_FILES: + case JobName.LIBRARY_SYNC_ASSET: + case JobName.LIBRARY_DELETE: + case JobName.SIDECAR_SYNC: + case JobName.SIDECAR_DISCOVERY: { + return { jobId: `${item.data.id}-${item.name}` }; + } + case JobName.LIBRARY_SYNC_FILE: { + return { jobId: `${item.data.id}-${item.data.assetPath}` }; + } case JobName.NOTIFY_ALBUM_UPDATE: { return { jobId: item.data.id, delay: item.data?.delay }; } @@ -228,6 +239,11 @@ export class JobRepository implements IJobRepository { case JobName.QUEUE_FACIAL_RECOGNITION: { return { jobId: JobName.QUEUE_FACIAL_RECOGNITION }; } + case JobName.LIBRARY_QUEUE_SYNC_ALL: + case JobName.LIBRARY_QUEUE_CLEANUP: { + // These jobs are globally unique and should only have one instance running at a time + return { jobId: item.name }; + } default: { return null; } diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index 9f60e35dcc..fb88b749c1 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -181,7 +181,6 @@ describe(LibraryService.name, () => { name: JobName.LIBRARY_SYNC_FILE, data: { id: libraryStub.externalLibrary1.id, - ownerId: libraryStub.externalLibrary1.owner.id, assetPath: '/data/user1/photo.jpg', }, }, @@ -401,7 +400,6 @@ describe(LibraryService.name, () => { it('should import a new asset', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: '/data/user1/photo.jpg', }; @@ -445,7 +443,6 @@ describe(LibraryService.name, () => { it('should import a new video', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: '/data/user1/video.mp4', }; @@ -489,7 +486,6 @@ describe(LibraryService.name, () => { it('should not import an asset to a soft deleted library', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: '/data/user1/photo.jpg', }; @@ -504,7 +500,6 @@ describe(LibraryService.name, () => { it('should not refresh a file whose mtime matches existing asset', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: assetStub.hasFileExtension.originalPath, }; @@ -522,10 +517,9 @@ describe(LibraryService.name, () => { expect(jobMock.queueAll).not.toHaveBeenCalled(); }); - it('should skip existing asset', async () => { + it('should skip an existing asset', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: '/data/user1/photo.jpg', }; @@ -537,7 +531,6 @@ describe(LibraryService.name, () => { it('should not refresh an asset trashed by user', async () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: mockUser.id, assetPath: assetStub.hasFileExtension.originalPath, }; @@ -554,7 +547,6 @@ describe(LibraryService.name, () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: userStub.admin.id, assetPath: '/data/user1/photo.jpg', }; @@ -572,7 +564,6 @@ describe(LibraryService.name, () => { const mockLibraryJob: ILibraryFileJob = { id: libraryStub.externalLibrary1.id, - ownerId: userStub.admin.id, assetPath: '/data/user1/photo.jpg', }; @@ -923,7 +914,6 @@ describe(LibraryService.name, () => { data: { id: libraryStub.externalLibraryWithImportPaths1.id, assetPath: '/foo/photo.jpg', - ownerId: libraryStub.externalLibraryWithImportPaths1.owner.id, }, }, ]); @@ -948,7 +938,6 @@ describe(LibraryService.name, () => { data: { id: libraryStub.externalLibraryWithImportPaths1.id, assetPath: '/foo/photo.jpg', - ownerId: libraryStub.externalLibraryWithImportPaths1.owner.id, }, }, ]); diff --git a/server/src/services/library.service.ts b/server/src/services/library.service.ts index 59ac171ce6..995dc931f2 100644 --- a/server/src/services/library.service.ts +++ b/server/src/services/library.service.ts @@ -228,14 +228,13 @@ export class LibraryService extends BaseService { return mapLibrary(library); } - private async syncFiles({ id, ownerId }: LibraryEntity, assetPaths: string[]) { + private async syncFiles({ id }: LibraryEntity, assetPaths: string[]) { await this.jobRepository.queueAll( assetPaths.map((assetPath) => ({ name: JobName.LIBRARY_SYNC_FILE, data: { id, assetPath, - ownerId, }, })), ); @@ -401,7 +400,7 @@ export class LibraryService extends BaseService { const mtime = stat.mtime; asset = await this.assetRepository.create({ - ownerId: job.ownerId, + ownerId: library.ownerId, libraryId: job.id, checksum: pathHash, originalPath: assetPath, @@ -433,12 +432,18 @@ export class LibraryService extends BaseService { async queueScan(id: string) { await this.findOrFail(id); + // We purge any existing scan jobs for this library. This is because the scan settings + // might have changed and the user wants to start a new scan with these settings. + await this.jobRepository.removeJob(id, JobName.LIBRARY_QUEUE_SYNC_FILES); + await this.jobRepository.removeJob(id, JobName.LIBRARY_QUEUE_SYNC_ASSETS); + await this.jobRepository.queue({ name: JobName.LIBRARY_QUEUE_SYNC_FILES, data: { id, }, }); + await this.jobRepository.queue({ name: JobName.LIBRARY_QUEUE_SYNC_ASSETS, data: { id } }); }