Merge pull request #3962 from bluewave-labs/fix/reset-job-state-on-success

fix: reset job state on success
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-09-15 10:18:28 -07:00
commit 580a025353
3 files changed
+56 -3

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
const LogsPage = () => {
const { t } = useTranslation();
const [activeTab, setActiveTab] = useState<number>(1);
const [activeTab, setActiveTab] = useState<number>(0);
return (
<BasePage headerKey="logs">
<Tabs
@@ -107,6 +107,8 @@ class MongoJobsRepository implements IJobsRepository {
lockedBy: null, // Remove lock
lockedUntil: null,
lastFinishedAt: now,
failCount: 0,
lastFailReason: null,
},
$inc: {
runCount: 1, // Increment run count
@@ -150,6 +152,8 @@ class MongoJobsRepository implements IJobsRepository {
lockedBy: null, // Remove lock
lockedUntil: null,
lastFinishedAt: now,
failCount: 0,
lastFailReason: null,
},
$inc: {
runCount: 1, // Increment run count
+51 -2
View File
@@ -3,7 +3,7 @@ import mongoose from "mongoose";
import { MongoMemoryServer } from "mongodb-memory-server";
import MongoJobsRepository from "../../src/domain/jobs/job.repository.mongo.ts";
import JobModel, { type JobDocument } from "../../src/domain/jobs/job.model.ts";
import { BACKOFF_MS, LOCK_MS, type Job, type JobType } from "../../src/domain/jobs/job.type.ts";
import { BACKOFF_MS, LOCK_MS, PARKED, type Job, type JobType } from "../../src/domain/jobs/job.type.ts";
// ── Real-Mongo harness ─────────────────────────────────────────────────────────
// The headline guarantee of the queue — "exactly one claimer wins" — IS MongoDB's
@@ -180,7 +180,7 @@ describe("MongoJobsRepository", () => {
it("does not release a lock owned by another worker (fenced no-op)", async () => {
// A worker whose lease expired and was reclaimed must not clobber the new owner's lock.
await seedJob({ lockedBy: "other-worker", lockedUntil: NOW + LOCK_MS, runCount: 7 });
await seedJob({ lockedBy: "other-worker", lockedUntil: NOW + LOCK_MS, runCount: 7, failCount: 2, lastFailReason: "timeout" });
const ok = await repo.recordSuccess("check:mon-1", NOW, 60_000, NOW + 1_000);
@@ -189,6 +189,55 @@ describe("MongoJobsRepository", () => {
expect(row?.lockedBy).toBe("other-worker"); // lock untouched
expect(row?.lockedUntil).toBe(NOW + LOCK_MS);
expect(row?.runCount).toBe(7); // not double-counted
expect(row?.failCount).toBe(2); // failure state untouched
expect(row?.lastFailReason).toBe("timeout");
});
it("clears the failure state so failCount counts consecutive failures", async () => {
await seedJob({ lockedBy: ownedBy(repo), lockedUntil: NOW + LOCK_MS, failCount: 3, lastFailReason: "connection refused" });
await repo.recordSuccess("check:mon-1", NOW, 60_000, NOW + 1_000);
const row = await readRow("check:mon-1");
expect(row?.failCount).toBe(0);
expect(row?.lastFailReason).toBeNull();
});
});
// ── recordOneShot (park after a single run) ─────────────────────────────────
describe("recordOneShot", () => {
it("parks the job, releases the lease, counts the run and clears the failure state", async () => {
await seedJob({
intervalMs: null,
lockedBy: ownedBy(repo),
lockedUntil: NOW + LOCK_MS,
runCount: 1,
failCount: 2,
lastFailReason: "connection refused",
});
const ok = await repo.recordOneShot("check:mon-1", NOW + 1_000);
expect(ok).toBe(true);
const row = await readRow("check:mon-1");
expect(row?.nextScheduledAt).toBe(PARKED);
expect(row?.lockedBy).toBeNull();
expect(row?.lockedUntil).toBeNull();
expect(row?.lastFinishedAt).toBe(NOW + 1_000);
expect(row?.runCount).toBe(2);
expect(row?.failCount).toBe(0);
expect(row?.lastFailReason).toBeNull();
});
it("does not touch a lock owned by another worker (fenced no-op)", async () => {
await seedJob({ intervalMs: null, lockedBy: "other-worker", lockedUntil: NOW + LOCK_MS, failCount: 2, lastFailReason: "timeout" });
expect(await repo.recordOneShot("check:mon-1", NOW + 1_000)).toBe(false);
const row = await readRow("check:mon-1");
expect(row?.nextScheduledAt).toBe(NOW); // not parked
expect(row?.lockedBy).toBe("other-worker");
expect(row?.failCount).toBe(2);
expect(row?.lastFailReason).toBe("timeout");
});
});