Skip to content

Commit f0e56f8

Browse files
committed
fix: Fix expired jobs with no retries get stuck in running state. fixes #4
1 parent 91dda28 commit f0e56f8

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

src/queue.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,4 +555,40 @@ describe("SqliteQueue", () => {
555555
failed: 0,
556556
});
557557
});
558+
559+
test("expired running job with no retries should be marked as failed", async () => {
560+
const queue = new SqliteQueue<Work>(
561+
"expired-job-queue",
562+
buildDBClient(":memory:", { runMigrations: true }),
563+
{
564+
defaultJobArgs: {
565+
numRetries: 0, // No retries
566+
},
567+
keepFailedJobs: true,
568+
},
569+
);
570+
571+
// Enqueue a job
572+
await queue.enqueue({ increment: 1 });
573+
574+
// Dequeue the job (makes it "running")
575+
const dequeuedJob = await queue.attemptDequeue({ timeoutSecs: 1 }); // Short timeout
576+
expect(dequeuedJob).not.toBeNull();
577+
expect(dequeuedJob!.status).toBe("running");
578+
expect(dequeuedJob!.numRunsLeft).toBe(0); // No retries left
579+
580+
// Wait for the job to expire
581+
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 1.5 seconds
582+
583+
// Try to dequeue again - should pick up the expired job for cleanup
584+
const expiredJob = await queue.attemptDequeue({ timeoutSecs: 5 });
585+
expect(expiredJob).toBeNull();
586+
587+
// Check stats - job should now be failed, not stuck in running
588+
const stats = await queue.stats();
589+
expect(stats.running).toBe(0);
590+
expect(stats.failed).toBe(1);
591+
expect(stats.pending).toBe(0);
592+
expect(stats.pending_retry).toBe(0);
593+
});
558594
});

src/queue.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ export class SqliteQueue<T> {
9191
.where(
9292
and(
9393
eq(tasksTable.queue, this.queueName),
94-
gt(tasksTable.numRunsLeft, 0),
9594
or(
9695
lte(tasksTable.availableAt, new Date()),
9796
isNull(tasksTable.availableAt),
@@ -120,6 +119,12 @@ export class SqliteQueue<T> {
120119
assert(jobs.length === 1);
121120
const job = jobs[0];
122121

122+
if (job.numRunsLeft === 0) {
123+
// Picked up an expired job
124+
await this.finalize(job.id, job.allocationId, "failed");
125+
return null;
126+
}
127+
123128
const result = await txn
124129
.update(tasksTable)
125130
.set({

0 commit comments

Comments
 (0)