mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
fix(server): dedupe database backup jobs (#28341)
* fix(server): dedupe database backup jobs via jobId #27268 shows backup jobs piling up in the queue across upgrades; one pending backup is always enough. * fix(tests): Avoid stale backup files from previous test runs being erroneously returned from createBackup * fix(jobs): Use bullmq's deduplication over jobId to avoid failed jobs from blocking future executions. --------- Co-authored-by: Robert Deaton <immich@rdeaton.space>
This commit is contained in:
@@ -2,7 +2,7 @@ import { LoginResponseDto, ManualJobName } from '@immich/sdk';
|
|||||||
import { errorDto } from 'src/responses';
|
import { errorDto } from 'src/responses';
|
||||||
import { app, utils } from 'src/utils';
|
import { app, utils } from 'src/utils';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
describe('/admin/database-backups', () => {
|
describe('/admin/database-backups', () => {
|
||||||
let cookie: string | undefined;
|
let cookie: string | undefined;
|
||||||
@@ -13,6 +13,9 @@ describe('/admin/database-backups', () => {
|
|||||||
admin = await utils.adminSetup({
|
admin = await utils.adminSetup({
|
||||||
onboarding: false,
|
onboarding: false,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
await utils.resetBackups(admin.accessToken);
|
await utils.resetBackups(admin.accessToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -568,6 +568,8 @@ export const utils = {
|
|||||||
name: ManualJobName.BackupDatabase,
|
name: ManualJobName.BackupDatabase,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(accessToken, 'backupDatabase');
|
||||||
|
|
||||||
return utils.poll(
|
return utils.poll(
|
||||||
() => request(app).get('/admin/database-backups').set('Authorization', `Bearer ${accessToken}`),
|
() => request(app).get('/admin/database-backups').set('Authorization', `Bearer ${accessToken}`),
|
||||||
({ status, body }) => status === 200 && body.backups.length === 1,
|
({ status, body }) => status === 200 && body.backups.length === 1,
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ export class JobRepository {
|
|||||||
options: this.getJobOptions(item) || undefined,
|
options: this.getJobOptions(item) || undefined,
|
||||||
} as JobItem & { data: any; options: JobsOptions | undefined };
|
} as JobItem & { data: any; options: JobsOptions | undefined };
|
||||||
|
|
||||||
if (job.options?.jobId) {
|
if (job.options?.jobId || job.options?.deduplication) {
|
||||||
// need to use add() instead of addBulk() for jobId deduplication
|
// need to use add() instead of addBulk() for jobId/deduplication to take effect
|
||||||
promises.push(this.getQueue(queueName).add(item.name, item.data, job.options));
|
promises.push(this.getQueue(queueName).add(item.name, item.data, job.options));
|
||||||
} else {
|
} else {
|
||||||
itemsByQueue[queueName] = itemsByQueue[queueName] || [];
|
itemsByQueue[queueName] = itemsByQueue[queueName] || [];
|
||||||
@@ -230,10 +230,13 @@ export class JobRepository {
|
|||||||
return { priority: 1 };
|
return { priority: 1 };
|
||||||
}
|
}
|
||||||
case JobName.FacialRecognitionQueueAll: {
|
case JobName.FacialRecognitionQueueAll: {
|
||||||
return { jobId: JobName.FacialRecognitionQueueAll };
|
return { deduplication: { id: JobName.FacialRecognitionQueueAll } };
|
||||||
}
|
}
|
||||||
case JobName.VersionCheck: {
|
case JobName.VersionCheck: {
|
||||||
return { jobId: JobName.VersionCheck };
|
return { deduplication: { id: JobName.VersionCheck } };
|
||||||
|
}
|
||||||
|
case JobName.DatabaseBackup: {
|
||||||
|
return { deduplication: { id: JobName.DatabaseBackup } };
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user