@quark-fw/plugin-cron 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/CronScheduler.test.ts +16 -0
- package/src/CronScheduler.ts +14 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quark-fw/plugin-cron",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"types": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -23,12 +23,12 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@quark-fw/atom": "0.1.
|
|
27
|
-
"@quark-fw/plugin": "0.1.
|
|
26
|
+
"@quark-fw/atom": "0.1.20",
|
|
27
|
+
"@quark-fw/plugin": "0.1.20"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@quark-fw/core": "0.1.
|
|
31
|
-
"@quark-fw/plugin-redis": "0.1.
|
|
30
|
+
"@quark-fw/core": "0.1.20",
|
|
31
|
+
"@quark-fw/plugin-redis": "0.1.20",
|
|
32
32
|
"ioredis": "^5.9.3"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
@@ -78,6 +78,22 @@ describe("CronScheduler", () => {
|
|
|
78
78
|
expect((await cron.status())[0]!.stale).toBe(true);
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
test("не запускавшаяся задача протухает только спустя maxAge после регистрации", async () => {
|
|
82
|
+
const { client } = fakeRedis();
|
|
83
|
+
let now = at("2026-10-06T12:00:00Z");
|
|
84
|
+
const cron = new CronScheduler({
|
|
85
|
+
client: () => client,
|
|
86
|
+
now: () => now,
|
|
87
|
+
prefix: "t:",
|
|
88
|
+
});
|
|
89
|
+
cron.schedule("job", "0 3 * * *", async () => {}, {
|
|
90
|
+
maxAgeMs: 3_600_000,
|
|
91
|
+
});
|
|
92
|
+
expect((await cron.status())[0]!.stale).toBe(false);
|
|
93
|
+
now = at("2026-10-06T13:30:00Z");
|
|
94
|
+
expect((await cron.status())[0]!.stale).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
81
97
|
test("лок в Redis: второй инстанс не запускает ту же задачу", async () => {
|
|
82
98
|
const { client } = fakeRedis();
|
|
83
99
|
const a = new CronScheduler({ client: () => client, prefix: "t:" });
|
package/src/CronScheduler.ts
CHANGED
|
@@ -43,7 +43,11 @@ export class CronScheduler {
|
|
|
43
43
|
private readonly onError: (job: string, error: unknown) => void;
|
|
44
44
|
private readonly jobs = new Map<
|
|
45
45
|
string,
|
|
46
|
-
CronJob & {
|
|
46
|
+
CronJob & {
|
|
47
|
+
fields: CronFields;
|
|
48
|
+
firedMinute?: string;
|
|
49
|
+
registeredAt: number;
|
|
50
|
+
}
|
|
47
51
|
>();
|
|
48
52
|
private readonly running = new Set<string>();
|
|
49
53
|
private timer: NodeJS.Timeout | undefined;
|
|
@@ -78,6 +82,7 @@ export class CronScheduler {
|
|
|
78
82
|
handler,
|
|
79
83
|
options: { lockTtlMs: DEFAULT_LOCK_TTL, ...options },
|
|
80
84
|
fields: parseCron(expression),
|
|
85
|
+
registeredAt: this.now().getTime(),
|
|
81
86
|
});
|
|
82
87
|
if (options.runOnStart) void this.run(name);
|
|
83
88
|
}
|
|
@@ -189,12 +194,17 @@ export class CronScheduler {
|
|
|
189
194
|
this.onError(job.name, error);
|
|
190
195
|
}
|
|
191
196
|
const lastSuccessAt = raw.lastSuccessAt || null;
|
|
197
|
+
// протухла: успех старше maxAge, а если успеха ещё не было — задача
|
|
198
|
+
// зарегистрирована дольше maxAge назад (свежий контейнер не виноват,
|
|
199
|
+
// что расписание ещё не наступило)
|
|
192
200
|
const stale =
|
|
193
201
|
job.options.maxAgeMs === undefined
|
|
194
202
|
? undefined
|
|
195
|
-
:
|
|
196
|
-
this.now().getTime() -
|
|
197
|
-
|
|
203
|
+
: lastSuccessAt
|
|
204
|
+
? this.now().getTime() -
|
|
205
|
+
new Date(lastSuccessAt).getTime() >
|
|
206
|
+
job.options.maxAgeMs
|
|
207
|
+
: this.now().getTime() - job.registeredAt >
|
|
198
208
|
job.options.maxAgeMs;
|
|
199
209
|
result.push({
|
|
200
210
|
name: job.name,
|