From dcc720a74ffa8f7d450d49d99a9eff6f3589a2ea Mon Sep 17 00:00:00 2001 From: Robert Deaton Date: Thu, 14 May 2026 23:01:33 -0700 Subject: [PATCH] fix(jobs): Use bullmq's deduplication to simplify album update and migration workers --- server/src/repositories/job.repository.ts | 13 ++----------- server/src/services/notification.service.spec.ts | 1 - server/src/services/notification.service.ts | 1 - server/test/repositories/job.repository.mock.ts | 1 - 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/server/src/repositories/job.repository.ts b/server/src/repositories/job.repository.ts index 5bb5276db7..9131b1cb38 100644 --- a/server/src/repositories/job.repository.ts +++ b/server/src/repositories/job.repository.ts @@ -219,12 +219,12 @@ export class JobRepository { switch (item.name) { case JobName.NotifyAlbumUpdate: { return { - jobId: `${item.data.id}/${item.data.recipientId}`, + deduplication: { id: `${item.data.id}/${item.data.recipientId}`, replace: true }, delay: item.data?.delay, }; } case JobName.StorageTemplateMigrationSingle: { - return { jobId: item.data.id }; + return { deduplication: { id: item.data.id } }; } case JobName.PersonGenerateThumbnail: { return { priority: 1 }; @@ -247,13 +247,4 @@ export class JobRepository { private getQueue(queue: QueueName): Queue { return this.moduleRef.get(getQueueToken(queue), { strict: false }); } - - /** @deprecated */ - // todo: remove this when asset notifications no longer need it. - public async removeJob(name: JobName, jobID: string): Promise { - const existingJob = await this.getQueue(this.getQueueName(name)).getJob(jobID); - if (existingJob) { - await existingJob.remove(); - } - } } diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index 0d51db7ac8..615e692294 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -523,7 +523,6 @@ describe(NotificationService.name, () => { it('should add new recipients for new images if job is already queued', async () => { await sut.onAlbumUpdate({ id: '1', recipientId: '2' } as INotifyAlbumUpdateJob); - expect(mocks.job.removeJob).toHaveBeenCalledWith(JobName.NotifyAlbumUpdate, '1/2'); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.NotifyAlbumUpdate, data: { diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index e688f7dc7f..eb30201dc0 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -218,7 +218,6 @@ export class NotificationService extends BaseService { @OnEvent({ name: 'AlbumUpdate' }) async onAlbumUpdate({ id, recipientId }: ArgOf<'AlbumUpdate'>) { - await this.jobRepository.removeJob(JobName.NotifyAlbumUpdate, `${id}/${recipientId}`); await this.jobRepository.queue({ name: JobName.NotifyAlbumUpdate, data: { id, recipientId, delay: NotificationService.albumUpdateEmailDelayMs }, diff --git a/server/test/repositories/job.repository.mock.ts b/server/test/repositories/job.repository.mock.ts index 4fc5460c8a..66769e8901 100644 --- a/server/test/repositories/job.repository.mock.ts +++ b/server/test/repositories/job.repository.mock.ts @@ -19,6 +19,5 @@ export const newJobRepositoryMock = (): Mocked