@iterativeflow/conformance 2.1.0 → 2.2.0
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/dist/index.d.mts +4 -0
- package/dist/index.mjs +71 -7
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -103,6 +103,10 @@ declare const shardedClaimConformance: (label: string, makeBackend: () => Backen
|
|
|
103
103
|
* `Store.dueCronCount(now, names)` — that `engine.pendingWork` composes. Every backend must count the
|
|
104
104
|
* same due work (claimable jobs, due timers, due crons), exclude leased/future work, and filter by
|
|
105
105
|
* flow name identically.
|
|
106
|
+
*
|
|
107
|
+
* A row whose run is gone counts under EVERY name filter, matching `Queue.claim` — a sharded fleet
|
|
108
|
+
* has to see the backlog it can actually lease, or it stays scaled to zero and the row never drains.
|
|
109
|
+
* An empty `names` is the exception: it means "this worker handles nothing", so it counts nothing.
|
|
106
110
|
*/
|
|
107
111
|
declare const pendingWorkConformance: (label: string, makeBackend: () => Backend$1 | Promise<Backend$1>) => void;
|
|
108
112
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -392,6 +392,46 @@ const storeConformance = (label, makeStore) => {
|
|
|
392
392
|
expect(await s.deleteRunsOlderThan(after, 1)).toBe(1);
|
|
393
393
|
expect(await s.deleteRunsOlderThan(after, 1)).toBe(0);
|
|
394
394
|
});
|
|
395
|
+
it("deleteRuns narrows by name/version/status, refuses live runs and an empty filter", async () => {
|
|
396
|
+
const s = await makeStore();
|
|
397
|
+
const old = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
398
|
+
const mk = async (name, version) => (await s.startRun({
|
|
399
|
+
name,
|
|
400
|
+
version,
|
|
401
|
+
input: {},
|
|
402
|
+
createdAt: old
|
|
403
|
+
})).runId;
|
|
404
|
+
const doomed = await mk("f", 1);
|
|
405
|
+
const otherVersion = await mk("f", 2);
|
|
406
|
+
const otherStatus = await mk("f", 1);
|
|
407
|
+
const otherName = await mk("g", 1);
|
|
408
|
+
const live = await mk("f", 1);
|
|
409
|
+
await s.markTerminal(doomed, { status: "canceled" });
|
|
410
|
+
await s.markTerminal(otherVersion, { status: "canceled" });
|
|
411
|
+
await s.markTerminal(otherStatus, {
|
|
412
|
+
status: "done",
|
|
413
|
+
output: 1
|
|
414
|
+
});
|
|
415
|
+
await s.markTerminal(otherName, { status: "canceled" });
|
|
416
|
+
expect(await s.deleteRuns({
|
|
417
|
+
name: "f",
|
|
418
|
+
version: 1,
|
|
419
|
+
status: "canceled"
|
|
420
|
+
}, 100)).toBe(1);
|
|
421
|
+
expect(await s.loadRunRow(doomed)).toBeUndefined();
|
|
422
|
+
for (const spared of [
|
|
423
|
+
otherVersion,
|
|
424
|
+
otherStatus,
|
|
425
|
+
otherName,
|
|
426
|
+
live
|
|
427
|
+
]) expect(await s.loadRunRow(spared)).toBeDefined();
|
|
428
|
+
expect(await s.deleteRuns({
|
|
429
|
+
status: "pending",
|
|
430
|
+
before: /* @__PURE__ */ new Date()
|
|
431
|
+
}, 100)).toBe(0);
|
|
432
|
+
expect((await s.loadRunRow(live))?.status).toBe("pending");
|
|
433
|
+
await expect(s.deleteRuns({}, 100)).rejects.toThrow();
|
|
434
|
+
});
|
|
395
435
|
it("checkpointStep on an unknown run is rejected (no orphan step)", async () => {
|
|
396
436
|
const s = await makeStore();
|
|
397
437
|
await expect(s.checkpointStep({
|
|
@@ -2306,7 +2346,7 @@ const claimFilterConformance = (label, makeBackend) => {
|
|
|
2306
2346
|
});
|
|
2307
2347
|
expect(onlyB.map((l) => l.runId)).toEqual([b.runId]);
|
|
2308
2348
|
});
|
|
2309
|
-
it("an empty set leases nothing; an omitted set leases everything", async () => {
|
|
2349
|
+
it("an empty set leases nothing — not even a run-less job; an omitted set leases everything", async () => {
|
|
2310
2350
|
const { store, queue } = await makeBackend();
|
|
2311
2351
|
const a = await store.startRun({
|
|
2312
2352
|
name: "flow-a",
|
|
@@ -2314,6 +2354,7 @@ const claimFilterConformance = (label, makeBackend) => {
|
|
|
2314
2354
|
input: {}
|
|
2315
2355
|
});
|
|
2316
2356
|
await queue.enqueue(a.runId);
|
|
2357
|
+
await queue.enqueue("orphan-run");
|
|
2317
2358
|
expect(await queue.claim({
|
|
2318
2359
|
limit: 10,
|
|
2319
2360
|
leaseMs: 1e3,
|
|
@@ -2325,7 +2366,24 @@ const claimFilterConformance = (label, makeBackend) => {
|
|
|
2325
2366
|
leaseMs: 1e3,
|
|
2326
2367
|
now: at(0)
|
|
2327
2368
|
});
|
|
2328
|
-
expect(all.map((l) => l.runId)).toEqual([a.runId]);
|
|
2369
|
+
expect(all.map((l) => l.runId).sort()).toEqual([a.runId, "orphan-run"].sort());
|
|
2370
|
+
});
|
|
2371
|
+
it("leases a run-less job under a name filter so it can be acked, not skipped forever", async () => {
|
|
2372
|
+
const { store, queue } = await makeBackend();
|
|
2373
|
+
const a = await store.startRun({
|
|
2374
|
+
name: "flow-a",
|
|
2375
|
+
version: 1,
|
|
2376
|
+
input: {}
|
|
2377
|
+
});
|
|
2378
|
+
await queue.enqueue(a.runId);
|
|
2379
|
+
await queue.enqueue("orphan-run");
|
|
2380
|
+
const leased = await queue.claim({
|
|
2381
|
+
limit: 10,
|
|
2382
|
+
leaseMs: 1e3,
|
|
2383
|
+
names: ["flow-a"],
|
|
2384
|
+
now: at(0)
|
|
2385
|
+
});
|
|
2386
|
+
expect(leased.map((l) => l.runId).sort()).toEqual([a.runId, "orphan-run"].sort());
|
|
2329
2387
|
});
|
|
2330
2388
|
});
|
|
2331
2389
|
};
|
|
@@ -2395,6 +2453,10 @@ const shardedClaimConformance = (label, makeBackend) => {
|
|
|
2395
2453
|
* `Store.dueCronCount(now, names)` — that `engine.pendingWork` composes. Every backend must count the
|
|
2396
2454
|
* same due work (claimable jobs, due timers, due crons), exclude leased/future work, and filter by
|
|
2397
2455
|
* flow name identically.
|
|
2456
|
+
*
|
|
2457
|
+
* A row whose run is gone counts under EVERY name filter, matching `Queue.claim` — a sharded fleet
|
|
2458
|
+
* has to see the backlog it can actually lease, or it stays scaled to zero and the row never drains.
|
|
2459
|
+
* An empty `names` is the exception: it means "this worker handles nothing", so it counts nothing.
|
|
2398
2460
|
*/
|
|
2399
2461
|
const pendingWorkConformance = (label, makeBackend) => {
|
|
2400
2462
|
describe(`pending-work conformance (${label})`, () => {
|
|
@@ -2424,6 +2486,7 @@ const pendingWorkConformance = (label, makeBackend) => {
|
|
|
2424
2486
|
const napping = await start("a");
|
|
2425
2487
|
await be.timer.schedule(napping, future);
|
|
2426
2488
|
await be.timer.schedule("orphan-run", past);
|
|
2489
|
+
await be.queue.enqueue("orphan-job");
|
|
2427
2490
|
const futureJob = await start("a");
|
|
2428
2491
|
await be.queue.enqueue(futureJob, { runAt: future });
|
|
2429
2492
|
await be.store.upsertCron({
|
|
@@ -2438,16 +2501,17 @@ const pendingWorkConformance = (label, makeBackend) => {
|
|
|
2438
2501
|
};
|
|
2439
2502
|
it("depth counts claimable jobs (not leased/future), filtered by flow name", async () => {
|
|
2440
2503
|
const be = await seed();
|
|
2441
|
-
expect((await be.queue.depth(now)).claimable).toBe(
|
|
2442
|
-
expect((await be.queue.depth(now, ["a"])).claimable).toBe(
|
|
2443
|
-
expect((await be.queue.depth(now, ["b"])).claimable).toBe(
|
|
2504
|
+
expect((await be.queue.depth(now)).claimable).toBe(3);
|
|
2505
|
+
expect((await be.queue.depth(now, ["a"])).claimable).toBe(2);
|
|
2506
|
+
expect((await be.queue.depth(now, ["b"])).claimable).toBe(2);
|
|
2444
2507
|
expect((await be.queue.depth(now, [])).claimable).toBe(0);
|
|
2445
2508
|
});
|
|
2446
2509
|
it("dueCount counts due timers incl. run-less (not future), filtered by flow name", async () => {
|
|
2447
2510
|
const be = await seed();
|
|
2448
2511
|
expect(await be.timer.dueCount(now)).toBe(2);
|
|
2449
|
-
expect(await be.timer.dueCount(now, ["a"])).toBe(
|
|
2450
|
-
expect(await be.timer.dueCount(now, ["b"])).toBe(
|
|
2512
|
+
expect(await be.timer.dueCount(now, ["a"])).toBe(2);
|
|
2513
|
+
expect(await be.timer.dueCount(now, ["b"])).toBe(1);
|
|
2514
|
+
expect(await be.timer.dueCount(now, [])).toBe(0);
|
|
2451
2515
|
});
|
|
2452
2516
|
it("dueCronCount counts due crons, filtered by flow name", async () => {
|
|
2453
2517
|
const be = await seed();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iterativeflow/conformance",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@iterativeflow/core": "2.
|
|
28
|
+
"@iterativeflow/core": "2.2.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"vitest": ">=3"
|