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:
morluto authored and GitHub committed 2026-11-11 18:34:32 +08:00
1 parent 94dd7b72f8
commit 2d80427422
2 files changed
+125 -7

No files matched your search

+103
View File
@@ -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) => {
+22 -7
View File
@@ -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);
}
};