effect-mq 0.6.0 → 0.7.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/JobStore.d.ts +41 -3
- package/dist/JobStore.d.ts.map +1 -1
- package/dist/JobStore.js +10 -0
- package/dist/JobStore.js.map +1 -1
- package/dist/MemoryJobStore.d.ts.map +1 -1
- package/dist/MemoryJobStore.js +27 -14
- package/dist/MemoryJobStore.js.map +1 -1
- package/dist/drizzle-postgres/DrizzleJobStore.d.ts.map +1 -1
- package/dist/drizzle-postgres/DrizzleJobStore.js +25 -3
- package/dist/drizzle-postgres/DrizzleJobStore.js.map +1 -1
- package/dist/redis/RedisJobStore.d.ts +53 -0
- package/dist/redis/RedisJobStore.d.ts.map +1 -1
- package/dist/redis/RedisJobStore.js +188 -37
- package/dist/redis/RedisJobStore.js.map +1 -1
- package/dist/redis/scripts.d.ts +101 -30
- package/dist/redis/scripts.d.ts.map +1 -1
- package/dist/redis/scripts.js +204 -55
- package/dist/redis/scripts.js.map +1 -1
- package/dist/testing/conformance.d.ts.map +1 -1
- package/dist/testing/conformance.js +127 -11
- package/dist/testing/conformance.js.map +1 -1
- package/package.json +1 -1
- package/src/JobStore.ts +38 -3
- package/src/MemoryJobStore.ts +26 -18
- package/src/drizzle-postgres/DrizzleJobStore.ts +27 -3
- package/src/redis/RedisJobStore.ts +256 -36
- package/src/redis/scripts.ts +232 -59
- package/src/testing/conformance.ts +143 -11
|
@@ -360,21 +360,137 @@ export const jobStoreConformance = (name, storeLayer) => {
|
|
|
360
360
|
})));
|
|
361
361
|
it.effect("list pagination is lossless when enqueue timestamps tie", () => withStore((store) => Effect.gen(function* () {
|
|
362
362
|
// No clock adjustment: every record shares one enqueuedAt, so
|
|
363
|
-
// ordering and the cursor fall back entirely to the id tie-break
|
|
363
|
+
// ordering and the cursor fall back entirely to the id tie-break —
|
|
364
|
+
// in both directions.
|
|
364
365
|
for (let i = 0; i < 7; i++) {
|
|
365
366
|
yield* store.enqueue(baseRequest({ payload: { n: i } }));
|
|
366
367
|
}
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
368
|
+
for (const order of ["desc", "asc"]) {
|
|
369
|
+
const seen = [];
|
|
370
|
+
let cursor;
|
|
371
|
+
do {
|
|
372
|
+
const page = yield* store.list({ limit: 3, order, cursor });
|
|
373
|
+
for (const item of page.items) {
|
|
374
|
+
expect(seen.includes(item.id)).toBe(false);
|
|
375
|
+
seen.push(item.id);
|
|
376
|
+
}
|
|
377
|
+
cursor = page.cursor;
|
|
378
|
+
} while (cursor !== undefined);
|
|
379
|
+
expect(seen.length).toBe(7);
|
|
380
|
+
// Opposite directions walk exact mirror orders.
|
|
381
|
+
if (order === "asc") {
|
|
382
|
+
const descAll = yield* store.list({ limit: 7, order: "desc" });
|
|
383
|
+
expect(seen).toEqual(descAll.items.map((item) => item.id).toReversed());
|
|
374
384
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
385
|
+
}
|
|
386
|
+
})));
|
|
387
|
+
it.effect("list orders by enqueuedAt ascending on request, cursor included", () => withStore((store) => Effect.gen(function* () {
|
|
388
|
+
const ids = [];
|
|
389
|
+
for (let i = 0; i < 3; i++) {
|
|
390
|
+
const { id } = yield* store.enqueue(baseRequest({ payload: { n: i } }));
|
|
391
|
+
ids.push(id);
|
|
392
|
+
yield* TestClock.adjust(1_000);
|
|
393
|
+
}
|
|
394
|
+
const first = yield* store.list({ orderBy: "enqueuedAt", order: "asc", limit: 2 });
|
|
395
|
+
expect(first.items.map((job) => job.id)).toEqual([ids[0], ids[1]]);
|
|
396
|
+
assert(first.cursor !== undefined);
|
|
397
|
+
const second = yield* store.list({
|
|
398
|
+
orderBy: "enqueuedAt",
|
|
399
|
+
order: "asc",
|
|
400
|
+
limit: 2,
|
|
401
|
+
cursor: first.cursor
|
|
402
|
+
});
|
|
403
|
+
expect(second.items.map((job) => job.id)).toEqual([ids[2]]);
|
|
404
|
+
expect(second.cursor).toBeUndefined();
|
|
405
|
+
})));
|
|
406
|
+
it.effect("list orders delayed jobs by runAt within a queue", () => withStore((store) => Effect.gen(function* () {
|
|
407
|
+
const late = yield* store.enqueue(baseRequest({ payload: { n: 1 }, delayMs: 30_000 }));
|
|
408
|
+
const soon = yield* store.enqueue(baseRequest({ payload: { n: 2 }, delayMs: 5_000 }));
|
|
409
|
+
const middle = yield* store.enqueue(baseRequest({ payload: { n: 3 }, delayMs: 10_000 }));
|
|
410
|
+
// An immediate job in the same queue must not appear.
|
|
411
|
+
yield* store.enqueue(baseRequest({ payload: { n: 4 } }));
|
|
412
|
+
const upcoming = yield* store.list({
|
|
413
|
+
queue: QueueName("default"),
|
|
414
|
+
states: ["delayed"],
|
|
415
|
+
orderBy: "runAt",
|
|
416
|
+
order: "asc",
|
|
417
|
+
limit: 2
|
|
418
|
+
});
|
|
419
|
+
expect(upcoming.items.map((job) => job.id)).toEqual([soon.id, middle.id]);
|
|
420
|
+
assert(upcoming.cursor !== undefined);
|
|
421
|
+
const rest = yield* store.list({
|
|
422
|
+
queue: QueueName("default"),
|
|
423
|
+
states: ["delayed"],
|
|
424
|
+
orderBy: "runAt",
|
|
425
|
+
order: "asc",
|
|
426
|
+
limit: 2,
|
|
427
|
+
cursor: upcoming.cursor
|
|
428
|
+
});
|
|
429
|
+
expect(rest.items.map((job) => job.id)).toEqual([late.id]);
|
|
430
|
+
expect(rest.cursor).toBeUndefined();
|
|
431
|
+
})));
|
|
432
|
+
it.effect("list orders terminal jobs by finishedAt, with and without a name", () => withStore((store) => Effect.gen(function* () {
|
|
433
|
+
// Enqueue in one order, finish in the REVERSE order, so finishedAt
|
|
434
|
+
// ordering and enqueuedAt ordering disagree — a driver that quietly
|
|
435
|
+
// ignores orderBy fails here instead of passing by coincidence.
|
|
436
|
+
const enqueueAs = (name, queue) => {
|
|
437
|
+
const request = queue === undefined
|
|
438
|
+
? baseRequest({ name })
|
|
439
|
+
: baseRequest({ name, queue: QueueName(queue) });
|
|
440
|
+
return Effect.map(store.enqueue(request), (result) => result.id);
|
|
441
|
+
};
|
|
442
|
+
const first = yield* enqueueAs("TestJob");
|
|
443
|
+
const second = yield* enqueueAs("OtherJob");
|
|
444
|
+
const third = yield* enqueueAs("TestJob");
|
|
445
|
+
const elsewhere = yield* enqueueAs("TestJob", "other");
|
|
446
|
+
const claims = new Map();
|
|
447
|
+
for (const token of ["t-1", "t-2", "t-3"]) {
|
|
448
|
+
const claim = yield* store.claim(claimOptions({
|
|
449
|
+
names: ["TestJob", "OtherJob"],
|
|
450
|
+
token
|
|
451
|
+
}));
|
|
452
|
+
assert(claim._tag === "Claimed");
|
|
453
|
+
claims.set(claim.job.id, token);
|
|
454
|
+
}
|
|
455
|
+
const otherClaim = yield* store.claim(claimOptions({
|
|
456
|
+
queue: QueueName("other"),
|
|
457
|
+
token: "t-4"
|
|
458
|
+
}));
|
|
459
|
+
assert(otherClaim._tag === "Claimed");
|
|
460
|
+
claims.set(elsewhere, "t-4");
|
|
461
|
+
for (const id of [elsewhere, third, second, first]) {
|
|
462
|
+
const token = claims.get(id);
|
|
463
|
+
assert(token !== undefined);
|
|
464
|
+
yield* store.ack(id, token, {
|
|
465
|
+
_tag: id === second ? "Fail" : "Complete",
|
|
466
|
+
exit: undefined
|
|
467
|
+
});
|
|
468
|
+
yield* TestClock.adjust(1_000);
|
|
469
|
+
}
|
|
470
|
+
// Across terminal states, newest FINISHED first: the reverse of
|
|
471
|
+
// enqueue order.
|
|
472
|
+
const recent = yield* store.list({
|
|
473
|
+
states: ["completed", "failed"],
|
|
474
|
+
orderBy: "finishedAt",
|
|
475
|
+
order: "desc"
|
|
476
|
+
});
|
|
477
|
+
expect(recent.items.map((job) => job.id)).toEqual([first, second, third, elsewhere]);
|
|
478
|
+
// Scoped to one name and state.
|
|
479
|
+
const byName = yield* store.list({
|
|
480
|
+
name: "TestJob",
|
|
481
|
+
states: ["completed"],
|
|
482
|
+
orderBy: "finishedAt",
|
|
483
|
+
order: "desc"
|
|
484
|
+
});
|
|
485
|
+
expect(byName.items.map((job) => job.id)).toEqual([first, third, elsewhere]);
|
|
486
|
+
// The queue filter applies on top of the finishedAt route.
|
|
487
|
+
const byQueue = yield* store.list({
|
|
488
|
+
queue: QueueName("other"),
|
|
489
|
+
states: ["completed"],
|
|
490
|
+
orderBy: "finishedAt",
|
|
491
|
+
order: "desc"
|
|
492
|
+
});
|
|
493
|
+
expect(byQueue.items.map((job) => job.id)).toEqual([elsewhere]);
|
|
378
494
|
})));
|
|
379
495
|
it.effect("keep count ties on finishedAt keep the most recently acked records", () => withStore((store) => Effect.gen(function* () {
|
|
380
496
|
// Two jobs acked at the SAME TestClock instant: the tie must break
|