mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
fix(native): preserve cancellation during signature admission (#10093)
* fix(native): preserve cancellation during snapshot admission * fix(native): retain cancellation during cleanup retry
This commit is contained in:
1 parent
94dd7b72f8
commit
2d80427422
2 files changed
+125
-7
No files matched your search
@@ -457,6 +457,109 @@ it.each([false, true])(
|
||||
},
|
||||
);
|
||||
|
||||
it("retains cancellation during failed cleanup retry without losing ownership", async () => {
|
||||
const target = await createTarget("same");
|
||||
const resources = new ArtifactResourceScope();
|
||||
const controller = new AbortController();
|
||||
const reason = { caller: "cancelled during cleanup retry" };
|
||||
const root = await createRoot();
|
||||
let attempts = 0;
|
||||
const create = async () => ({
|
||||
path: root.path,
|
||||
async close() {
|
||||
attempts += 1;
|
||||
if (attempts === 2) controller.abort(reason);
|
||||
if (attempts <= 2) throw new Error("controlled root close failure");
|
||||
await root.close();
|
||||
},
|
||||
});
|
||||
try {
|
||||
const first = await withSignatureTarget(
|
||||
target,
|
||||
resources,
|
||||
async () => ok(signatureInspectionFixture()),
|
||||
undefined,
|
||||
create,
|
||||
);
|
||||
if (first.ok) throw new Error("Expected retained root");
|
||||
const second = await withSignatureTarget(
|
||||
target,
|
||||
resources,
|
||||
unusedInspection,
|
||||
controller.signal,
|
||||
create,
|
||||
);
|
||||
if (second.ok) throw new Error("Expected failed cleanup retry");
|
||||
expect(second.error.cleanupIncomplete).toBe(true);
|
||||
expect(second.error.cleanupResources).toContain(root.path);
|
||||
expect(second.error.cleanupPrimary?.secondaryFailures).toContainEqual(
|
||||
expect.objectContaining({
|
||||
_tag: "AnalysisCancelledError",
|
||||
cause: reason,
|
||||
}),
|
||||
);
|
||||
await resources.close();
|
||||
expect(first.error.cleanupIncomplete).toBe(false);
|
||||
expect(second.error.cleanupIncomplete).toBe(false);
|
||||
await expect(stat(root.path)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
} finally {
|
||||
await resources.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("retains late cancellation when creating the signature root fails", async () => {
|
||||
const target = await createTarget("same");
|
||||
const resources = new ArtifactResourceScope();
|
||||
const controller = new AbortController();
|
||||
const reason = { caller: "cancelled during root creation" };
|
||||
const physical = Object.assign(new Error("denied root creation"), {
|
||||
code: "EACCES",
|
||||
});
|
||||
const result = await withSignatureTarget(
|
||||
target,
|
||||
resources,
|
||||
unusedInspection,
|
||||
controller.signal,
|
||||
async () => {
|
||||
controller.abort(reason);
|
||||
throw physical;
|
||||
},
|
||||
);
|
||||
if (result.ok) throw new Error("Expected root creation failure");
|
||||
expect(result.error.cause).toBe(physical);
|
||||
expect(result.error.secondaryFailures).toHaveLength(1);
|
||||
expect(result.error.secondaryFailures[0]).toMatchObject({
|
||||
_tag: "AnalysisCancelledError",
|
||||
cause: reason,
|
||||
});
|
||||
expect(result.error.cleanupIncomplete).toBe(false);
|
||||
await resources.close();
|
||||
});
|
||||
|
||||
it("does not adopt a typed caller abort reason as a root-creation failure", async () => {
|
||||
const target = await createTarget("same");
|
||||
const resources = new ArtifactResourceScope();
|
||||
const controller = new AbortController();
|
||||
const reason = new ProviderCleanupError("foreign", ["unowned"], {});
|
||||
const result = await withSignatureTarget(
|
||||
target,
|
||||
resources,
|
||||
unusedInspection,
|
||||
controller.signal,
|
||||
async () => {
|
||||
controller.abort(reason);
|
||||
throw reason;
|
||||
},
|
||||
);
|
||||
if (result.ok) throw new Error("Expected cancellation");
|
||||
expect(result.error).toMatchObject({
|
||||
_tag: "AnalysisCancelledError",
|
||||
cause: reason,
|
||||
});
|
||||
expect(result.error.cleanupResources).not.toContain("unowned");
|
||||
await resources.close();
|
||||
});
|
||||
|
||||
it.each([false, true])(
|
||||
"retains late cancellation after a signature failure while root cleanup fails: %s",
|
||||
async (cleanupFails) => {
|
||||
|
||||
@@ -114,10 +114,19 @@ export const withSignatureTarget = async (
|
||||
assertRegularFileActive(signal);
|
||||
root = await createRoot();
|
||||
} catch (cause: unknown) {
|
||||
return err(
|
||||
const failure =
|
||||
cause instanceof RegularFileOperationCancelled
|
||||
? signatureReadFailure(target, cause)
|
||||
: snapshotFailure(target, "create", cause),
|
||||
: snapshotFailure(target, "create", cause);
|
||||
return err(
|
||||
signal?.aborted
|
||||
? analysisErrorWithCancellation(
|
||||
cause === signal.reason ? undefined : failure,
|
||||
new AnalysisCancelledError("inspect_signature", {
|
||||
cause: signal.reason,
|
||||
}),
|
||||
)
|
||||
: failure,
|
||||
);
|
||||
}
|
||||
const children = new ArtifactResourceScope();
|
||||
@@ -186,6 +195,15 @@ export const withSignatureTarget = async (
|
||||
return result;
|
||||
});
|
||||
} catch (cause: unknown) {
|
||||
const primary = signatureReadFailure(target, cause);
|
||||
const outcome = signal?.aborted
|
||||
? analysisErrorWithCancellation(
|
||||
cause === signal.reason ? undefined : primary,
|
||||
new AnalysisCancelledError("inspect_signature", {
|
||||
cause: signal.reason,
|
||||
}),
|
||||
)
|
||||
: primary;
|
||||
const retained =
|
||||
cause instanceof ArtifactReaderFailure && cause.cleanup !== undefined
|
||||
? cause.cleanup
|
||||
@@ -198,12 +216,9 @@ export const withSignatureTarget = async (
|
||||
"retry",
|
||||
);
|
||||
bindArtifactFileCleanup(cause, failure);
|
||||
return combineCleanupFailure(
|
||||
err(signatureReadFailure(target, cause)),
|
||||
failure,
|
||||
);
|
||||
return combineCleanupFailure(err(outcome), failure);
|
||||
}
|
||||
return err(signatureReadFailure(target, cause));
|
||||
return err(outcome);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user