fix: serialize mirror copy operations per destination repository
This commit is contained in:
parent
bae43c0d90
commit
955f77bb0b
2 changed files with 57 additions and 1 deletions
|
|
@ -525,4 +525,60 @@ describe("mirror operations", () => {
|
|||
// assert
|
||||
expect(resticForgetMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should serialize mirror copies for schedules that share the same mirror repository", async () => {
|
||||
const { resticCopyMock } = setup();
|
||||
const sourceRepository = await createTestRepository();
|
||||
const mirrorRepository = await createTestRepository();
|
||||
const firstVolume = await createTestVolume();
|
||||
const secondVolume = await createTestVolume();
|
||||
const firstSchedule = await createTestBackupSchedule({
|
||||
volumeId: firstVolume.id,
|
||||
repositoryId: sourceRepository.id,
|
||||
});
|
||||
const secondSchedule = await createTestBackupSchedule({
|
||||
volumeId: secondVolume.id,
|
||||
repositoryId: sourceRepository.id,
|
||||
});
|
||||
|
||||
await createTestBackupScheduleMirror(firstSchedule.id, mirrorRepository.id);
|
||||
await createTestBackupScheduleMirror(secondSchedule.id, mirrorRepository.id);
|
||||
|
||||
let releaseFirstCopy = () => {};
|
||||
let resolveFirstCopyStarted = () => {};
|
||||
const firstCopyStarted = new Promise<void>((resolve) => {
|
||||
resolveFirstCopyStarted = resolve;
|
||||
});
|
||||
|
||||
resticCopyMock.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveFirstCopyStarted();
|
||||
releaseFirstCopy = () => resolve({ success: true, output: "" });
|
||||
}),
|
||||
);
|
||||
resticCopyMock.mockImplementation(() => Promise.resolve({ success: true, output: "" }));
|
||||
|
||||
const firstCopyPromise = backupsExecutionService.copyToMirrors(firstSchedule.id, sourceRepository, null);
|
||||
await firstCopyStarted;
|
||||
|
||||
const secondCopyPromise = backupsExecutionService.copyToMirrors(secondSchedule.id, sourceRepository, null);
|
||||
|
||||
try {
|
||||
const secondCopyState = await Promise.race<"resolved" | "timeout">([
|
||||
secondCopyPromise.then(() => "resolved"),
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => resolve("timeout"), 50);
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(secondCopyState).toBe("timeout");
|
||||
expect(resticCopyMock).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
releaseFirstCopy();
|
||||
await Promise.all([firstCopyPromise, secondCopyPromise]);
|
||||
}
|
||||
|
||||
expect(resticCopyMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ async function copyToSingleMirror(
|
|||
});
|
||||
|
||||
const releaseSource = await repoMutex.acquireShared(sourceRepository.id, `mirror_source:${scheduleId}`);
|
||||
const releaseMirror = await repoMutex.acquireShared(mirror.repository.id, `mirror:${scheduleId}`);
|
||||
const releaseMirror = await repoMutex.acquireExclusive(mirror.repository.id, `mirror:${scheduleId}`);
|
||||
|
||||
try {
|
||||
await restic.copy(sourceRepository.config, mirror.repository.config, { tag: schedule.shortId, organizationId });
|
||||
|
|
|
|||
Loading…
Reference in a new issue