fix: 隔离 Queue 重试内部 Job identity

This commit is contained in:
root
2026-07-12 02:55:00 +02:00
parent 3eec6d6da2
commit 810adfa02a
3 changed files with 22 additions and 5 deletions
+7 -1
View File
@@ -13,6 +13,7 @@ export interface DispatchQueueTaskOptions {
}
export async function dispatchQueueTask(options: DispatchQueueTaskOptions): Promise<QueueDispatchResult> {
assertPublicQueueDispatchInput(options.input);
const task = await options.store.getQueueTask(options.taskId);
assertDispatchable(task);
@@ -140,7 +141,6 @@ export function buildQueueRunnerJobInput(task: QueueTaskRecord, commandId: strin
copyString("image", "image");
copyString("namespace", "namespace");
copyString("attemptId", "attemptId");
copyString("runnerJobId", "runnerJobId");
copyString("runnerId", "runnerId");
copyString("sourceCommit", "sourceCommit");
copyString("serviceAccountName", "serviceAccountName");
@@ -153,6 +153,12 @@ export function buildQueueRunnerJobInput(task: QueueTaskRecord, commandId: strin
return jobInput;
}
function assertPublicQueueDispatchInput(input: JsonRecord): void {
if (Object.prototype.hasOwnProperty.call(input, "runnerJobId")) {
throw new AgentRunError("schema-invalid", "queue dispatch does not accept caller-provided runnerJobId", { httpStatus: 400, details: { unexpected: ["runnerJobId"], valuesPrinted: false } });
}
}
function stringFrom(record: JsonRecord, key: string): string {
const value = record[key];
if (typeof value !== "string" || value.length === 0) throw new AgentRunError("infra-failed", `runner job response missing ${key}`, { httpStatus: 502 });
+6 -4
View File
@@ -39,11 +39,13 @@ export async function retryQueueTask(options: RetryQueueTaskOptions): Promise<Qu
runnerJob = await createKubernetesRunnerJob({
store: options.store,
runId: run.id,
input: buildQueueRunnerJobInput(reservation.task, command.id, {
attemptId: attempt.attemptId,
input: {
...buildQueueRunnerJobInput(reservation.task, command.id, {
attemptId: attempt.attemptId,
idempotencyKey: `queue:${reservation.task.id}:attempt:${attempt.attemptId}:runner-job`,
}),
runnerJobId: attempt.runnerJobId,
idempotencyKey: `queue:${reservation.task.id}:attempt:${attempt.attemptId}:runner-job`,
}),
},
defaults: options.defaults,
});
} catch (error) {
+9
View File
@@ -76,6 +76,14 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
});
try {
const client = new ManagerClient(server.baseUrl);
const injectionTask = await client.post("/api/v1/queue/tasks", taskInput(context.workspace, "queue-q5-runner-job-id-injection")) as QueueTaskRecord;
await assert.rejects(
() => client.post(`/api/v1/queue/tasks/${injectionTask.id}/dispatch`, { attemptId: "attempt_public_injection", runnerJobId: "rjob_public_injection" }),
/does not accept caller-provided runnerJobId/u,
);
assert.equal((await client.get(`/api/v1/queue/tasks/${injectionTask.id}`) as QueueTaskRecord).state, "pending");
assert.equal((await attempts(client, injectionTask.id)).count, 0);
const failed = await createTerminalTask(client, context.workspace, "failed", "queue-q5-failed");
const before = await attempts(client, failed.task.id);
assert.equal(before.count, 1);
@@ -215,6 +223,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
tests: [
"retry-server-dry-run-zero-write",
"initial-dispatch-attempt-history",
"public-dispatch-rejects-runner-job-identity-injection",
"retry-rejects-task-patch",
"retry-same-key-concurrency",
"retry-job-failure-keeps-task-terminal",