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
|
// assert
|
||||||
expect(resticForgetMock).not.toHaveBeenCalled();
|
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 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 {
|
try {
|
||||||
await restic.copy(sourceRepository.config, mirror.repository.config, { tag: schedule.shortId, organizationId });
|
await restic.copy(sourceRepository.config, mirror.repository.config, { tag: schedule.shortId, organizationId });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue