Merge pull request #3962 from bluewave-labs/fix/reset-job-state-on-success
fix: reset job state on success
This commit is contained in:
3 files changed
+56
-3
No files matched your search
@@ -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
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user