From d72eb62fbad2ff9ddea6ad07bffc65ec953db1a6 Mon Sep 17 00:00:00 2001 From: "zenrows-dev-pool[bot]" Date: Fri, 30 Oct 2026 10:58:53 +0000 Subject: [PATCH] fix(batch): stop a wait before a back-off that ends on its --timeout waitForJob slept whenever the next back-off ended at or before the deadline, so a back-off ending exactly on it slept the whole way and then read the job with no time left. Whether that happened depended on whether a millisecond passed between reads, which made "a wait never sleeps or retries past its --timeout" fail now and then. The wait now stops when the back-off would reach the deadline, as the retry inside each read already does. --- src/core/batch-api.ts | 2 +- tests/batch-consistency.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/core/batch-api.ts b/src/core/batch-api.ts index 115e0dd..8089e3c 100644 --- a/src/core/batch-api.ts +++ b/src/core/batch-api.ts @@ -830,7 +830,7 @@ export async function waitForJob(id: string, opts: CallOpts & { waitMs?: number if (!isTransient(err)) throw err; lastErr = err; } - if (Date.now() + delay > deadline) { + if (Date.now() + delay >= deadline) { // Out of time: report the last state read, or the error if none was. if (!last) throw lastErr; return last; diff --git a/tests/batch-consistency.test.ts b/tests/batch-consistency.test.ts index 37e8911..ad43e77 100644 --- a/tests/batch-consistency.test.ts +++ b/tests/batch-consistency.test.ts @@ -706,6 +706,31 @@ test("the repeat command shell-quotes every argument and the key, keeping inner ); }); +test("a back-off that ends exactly at the --timeout is not slept", async () => { + // A clock that moves only by each sleep, so no stray millisecond decides it: + // after the 2 s back-off, the next 3 s one would end on the 5 s deadline + // itself, with no time left to read the job. + const realNow = Date.now; + let clock = realNow(); + Date.now = () => clock; + try { + const sleeps: number[] = []; + const impl = (async () => json(job())) as unknown as typeof fetch; + await waitForJob("j1", { + apiKey: "k", + fetchImpl: impl, + waitMs: 5000, + sleepImpl: async (ms) => { + sleeps.push(ms); + clock += ms; + }, + }); + assert.deepEqual(sleeps, [2000], `sleeps: ${sleeps}`); + } finally { + Date.now = realNow; + } +}); + test("a wait never sleeps or retries past its --timeout", async () => { const sleeps: number[] = []; let n = 0;