





























@@ -615,6 +615,74 @@ describe("cron service timer regressions", () => {
615615}
616616});
617617618+it("does not spend isolated execution timeout while waiting for the runner lane (#41783)", async () => {
619+vi.useFakeTimers();
620+try {
621+const store = timerRegressionFixtures.makeStorePath();
622+const scheduledAt = Date.parse("2026-02-15T13:00:00.000Z");
623+const cronJob = createIsolatedRegressionJob({
624+id: "timeout-after-lane-start",
625+name: "timeout after lane start",
626+ scheduledAt,
627+schedule: { kind: "at", at: new Date(scheduledAt).toISOString() },
628+payload: { kind: "agentTurn", message: "work", timeoutSeconds: FAST_TIMEOUT_SECONDS },
629+state: { nextRunAtMs: scheduledAt },
630+});
631+await writeCronJobs(store.storePath, [cronJob]);
632+633+let now = scheduledAt;
634+const runnerEntered = createDeferred<void>();
635+const laneAcquired = createDeferred<void>();
636+let observedAbortSignal: AbortSignal | undefined;
637+const state = createCronServiceState({
638+cronEnabled: true,
639+storePath: store.storePath,
640+log: noopLogger,
641+nowMs: () => now,
642+enqueueSystemEvent: vi.fn(),
643+requestHeartbeatNow: vi.fn(),
644+runIsolatedAgentJob: vi.fn(async ({ abortSignal, onExecutionStarted }) => {
645+observedAbortSignal = abortSignal;
646+runnerEntered.resolve();
647+await laneAcquired.promise;
648+onExecutionStarted?.();
649+await new Promise<void>((resolve) => {
650+if (!abortSignal) {
651+resolve();
652+return;
653+}
654+if (abortSignal.aborted) {
655+resolve();
656+return;
657+}
658+abortSignal.addEventListener("abort", () => resolve(), { once: true });
659+});
660+now += 5;
661+return { status: "ok" as const, summary: "late" };
662+}),
663+});
664+665+const timerPromise = onTimer(state);
666+await runnerEntered.promise;
667+await vi.advanceTimersByTimeAsync(Math.ceil(FAST_TIMEOUT_SECONDS * 1_000) + 10);
668+expect(observedAbortSignal?.aborted).toBe(false);
669+670+laneAcquired.resolve();
671+await Promise.resolve();
672+expect(observedAbortSignal?.aborted).toBe(false);
673+674+await vi.advanceTimersByTimeAsync(Math.ceil(FAST_TIMEOUT_SECONDS * 1_000) + 10);
675+await timerPromise;
676+677+expect(observedAbortSignal?.aborted).toBe(true);
678+const job = state.store?.jobs.find((entry) => entry.id === "timeout-after-lane-start");
679+expect(job?.state.lastStatus).toBe("error");
680+expect(job?.state.lastError).toContain("timed out");
681+} finally {
682+vi.useRealTimers();
683+}
684+});
685+618686it("suppresses isolated follow-up side effects after timeout", async () => {
619687vi.useFakeTimers();
620688try {
@@ -981,30 +1049,39 @@ describe("cron service timer regressions", () => {
9811049nowMs: () => now,
9821050enqueueSystemEvent: vi.fn(),
9831051requestHeartbeatNow: vi.fn(),
984-runIsolatedAgentJob: vi.fn(async ({ abortSignal }: { abortSignal?: AbortSignal }) => {
985-started.resolve();
986-await new Promise<void>((resolve) => {
987-if (!abortSignal) {
988-resolve();
989-return;
990-}
991-if (abortSignal.aborted) {
992-abortWallMs = Date.now();
993-resolve();
994-return;
995-}
996-abortSignal.addEventListener(
997-"abort",
998-() => {
1052+runIsolatedAgentJob: vi.fn(
1053+async ({
1054+ abortSignal,
1055+ onExecutionStarted,
1056+}: {
1057+abortSignal?: AbortSignal;
1058+onExecutionStarted?: () => void;
1059+}) => {
1060+onExecutionStarted?.();
1061+started.resolve();
1062+await new Promise<void>((resolve) => {
1063+if (!abortSignal) {
1064+resolve();
1065+return;
1066+}
1067+if (abortSignal.aborted) {
9991068abortWallMs = Date.now();
10001069resolve();
1001-},
1002-{ once: true },
1003-);
1004-});
1005-now += 5;
1006-return { status: "ok" as const, summary: "done" };
1007-}),
1070+return;
1071+}
1072+abortSignal.addEventListener(
1073+"abort",
1074+() => {
1075+abortWallMs = Date.now();
1076+resolve();
1077+},
1078+{ once: true },
1079+);
1080+});
1081+now += 5;
1082+return { status: "ok" as const, summary: "done" };
1083+},
1084+),
10081085});
1009108610101087const timerPromise = onTimer(state);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。