@ddd-ts-kit/redis 0.9.0 → 0.11.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/README.md +33 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs/claimed-job-of-record.d.ts +5 -0
- package/dist/jobs/claimed-job-of-record.d.ts.map +1 -0
- package/dist/jobs/claimed-job-of-record.js +23 -0
- package/dist/jobs/claimed-job-of-record.js.map +1 -0
- package/dist/jobs/job-scripts.d.ts +13 -0
- package/dist/jobs/job-scripts.d.ts.map +1 -0
- package/dist/jobs/job-scripts.js +84 -0
- package/dist/jobs/job-scripts.js.map +1 -0
- package/dist/jobs/parse-enqueued-by.d.ts +4 -0
- package/dist/jobs/parse-enqueued-by.d.ts.map +1 -0
- package/dist/jobs/parse-enqueued-by.js +23 -0
- package/dist/jobs/parse-enqueued-by.js.map +1 -0
- package/dist/jobs/parse-job-record.d.ts +4 -0
- package/dist/jobs/parse-job-record.d.ts.map +1 -0
- package/dist/jobs/parse-job-record.js +17 -0
- package/dist/jobs/parse-job-record.js.map +1 -0
- package/dist/jobs/queued-job-of-record.d.ts +5 -0
- package/dist/jobs/queued-job-of-record.d.ts.map +1 -0
- package/dist/jobs/queued-job-of-record.js +18 -0
- package/dist/jobs/queued-job-of-record.js.map +1 -0
- package/dist/jobs/redis-job-record.d.ts +25 -0
- package/dist/jobs/redis-job-record.d.ts.map +1 -0
- package/dist/jobs/redis-job-record.js +2 -0
- package/dist/jobs/redis-job-record.js.map +1 -0
- package/dist/jobs/redis-job-store-options.d.ts +11 -0
- package/dist/jobs/redis-job-store-options.d.ts.map +1 -0
- package/dist/jobs/redis-job-store-options.js +2 -0
- package/dist/jobs/redis-job-store-options.js.map +1 -0
- package/dist/jobs/redis-job-store.d.ts +48 -0
- package/dist/jobs/redis-job-store.d.ts.map +1 -0
- package/dist/jobs/redis-job-store.js +178 -0
- package/dist/jobs/redis-job-store.js.map +1 -0
- package/dist/jobs/report-lost-jobs.d.ts +10 -0
- package/dist/jobs/report-lost-jobs.d.ts.map +1 -0
- package/dist/jobs/report-lost-jobs.js +18 -0
- package/dist/jobs/report-lost-jobs.js.map +1 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @ddd-ts-kit/redis
|
|
2
2
|
|
|
3
3
|
Adapters of kernel ports on the Redis protocol: the `Cache`, `Lock` and `RateLimiter` ports,
|
|
4
|
-
the wake-up signal of the outbox relay, and an integration event
|
|
4
|
+
the wake-up signal of the outbox relay, a store of background jobs, and an integration event
|
|
5
|
+
provider on Redis Streams.
|
|
5
6
|
They use a common subset of the protocol, so the same code runs on Redis, Dragonfly and
|
|
6
7
|
Valkey: the choice of server is an operational one, made in the composition root by a
|
|
7
8
|
connection URL.
|
|
@@ -12,7 +13,7 @@ npm i @ddd-ts-kit/redis ioredis
|
|
|
12
13
|
|
|
13
14
|
| Entry | Holds | Imports |
|
|
14
15
|
|---|---|---|
|
|
15
|
-
| `@ddd-ts-kit/redis` | `connectRedis`, `redisHealth`, `RedisCache`, `RedisLock`, `RedisRateLimiter`, `RedisRelaySignal`, `relayWakeUpMiddleware`; re-exported from the kernel: the `Lock` and `RateLimiter` ports, `InMemoryLock`, `InMemoryRateLimiter` | the kernel, `ioredis` (peer) |
|
|
16
|
+
| `@ddd-ts-kit/redis` | `connectRedis`, `redisHealth`, `RedisCache`, `RedisLock`, `RedisRateLimiter`, `RedisRelaySignal`, `relayWakeUpMiddleware`, `RedisJobStore`, `reportLostJobs`; re-exported from the kernel: the `Lock` and `RateLimiter` ports, `InMemoryLock`, `InMemoryRateLimiter` | the kernel, `@ddd-ts-kit/scheduling`, `ioredis` (peer) |
|
|
16
17
|
| `@ddd-ts-kit/redis/event-bus` | `RedisStreamsEventBus`, its options, topology and error types, the names of the entry fields | the kernel, `ioredis` (peer) |
|
|
17
18
|
| `@ddd-ts-kit/redis/testing` | `cacheContract`, `lockContract`, `rateLimiterContract`, re-exported from `@ddd-ts-kit/testing/contracts` | `@ddd-ts-kit/testing` (optional peer), the runner you pass |
|
|
18
19
|
|
|
@@ -665,6 +666,36 @@ timeout, groups lost and created again, reconnection through a TCP relay the tes
|
|
|
665
666
|
graceful close) and the whole chain from the outbox to an idempotent consumer. Each test works
|
|
666
667
|
under a prefix of its own and deletes its keys.
|
|
667
668
|
|
|
669
|
+
## Background job store
|
|
670
|
+
|
|
671
|
+
`RedisJobStore` is the `JobStore` of `@ddd-ts-kit/scheduling` on Redis, Dragonfly or Valkey,
|
|
672
|
+
for a project without a database or a queue it wants out of it: a few Lua scripts on the
|
|
673
|
+
client of the package, every key under `{<namespace>}` so that a cluster keeps them in one slot
|
|
674
|
+
and Dragonfly sees every key a script touches declared.
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
const jobStore = new RedisJobStore(redis, { namespace: 'jobs', onLostJob: reportLostJobs(logger, errors) });
|
|
678
|
+
|
|
679
|
+
kernelModule({ jobStore });
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
`reportLostJobs(logger, errors?)` logs a write lost after its unit of work committed (the id of
|
|
683
|
+
the job, the class and code of the error) and hands the error to the `ErrorReporter`, origin
|
|
684
|
+
`queue`; a project initialised with `--queue redis` has it bound by its platform module.
|
|
685
|
+
|
|
686
|
+
Redis cannot join the transaction of a database. Inside a unit of work, `add`, `cancel` and
|
|
687
|
+
`retryDead` answer from what Redis holds and write once it committed (`whenCommitted` of the
|
|
688
|
+
kernel): a command that rolls back leaves no job and cancels none, but a process that dies
|
|
689
|
+
between the commit and the write loses that write, and `onLostJob` is told when it fails. With
|
|
690
|
+
a database, prefer the jobs table of the ORM package. The idempotency key, scoped by the tenant
|
|
691
|
+
of the job, is checked before the job is staged and again when it is written: two jobs of one
|
|
692
|
+
key staged before either committed both answer their own id, and only the first is kept.
|
|
693
|
+
|
|
694
|
+
Not BullMQ: it would bring a second worker model that the store contract (claim, lease,
|
|
695
|
+
heartbeat, reports by attempt) does not map onto, and emulated cluster mode on Dragonfly. The
|
|
696
|
+
store passes `jobStoreContract` on Dragonfly in the integration job of CI; it was also run by
|
|
697
|
+
hand on Redis 8 (last run: Dragonfly 2.0.2, Redis 8.2, 2026-10-13). What the jobs guarantee: [Background jobs](../../docs/concepts/background-jobs.md).
|
|
698
|
+
|
|
668
699
|
## Contract suites
|
|
669
700
|
|
|
670
701
|
Each port has a suite that its in-memory implementation and its adapter both pass. An adapter
|
package/dist/index.d.ts
CHANGED
|
@@ -14,4 +14,7 @@ export { RedisRelaySignal, type RedisRelaySignalOptions } from './relay/redis-re
|
|
|
14
14
|
export { relayWakeUpMiddleware } from './relay/relay-wake-up-middleware.js';
|
|
15
15
|
export { InMemoryLock, Lock, type AcquireOptions, type LockHandle, type WaitingOptions } from '@ddd-ts-kit/kernel';
|
|
16
16
|
export { InMemoryRateLimiter, RateLimiter, type RateLimit, type RateLimitDecision } from '@ddd-ts-kit/kernel';
|
|
17
|
+
export { RedisJobStore } from './jobs/redis-job-store.js';
|
|
18
|
+
export type { RedisJobStoreOptions } from './jobs/redis-job-store-options.js';
|
|
19
|
+
export { reportLostJobs } from './jobs/report-lost-jobs.js';
|
|
17
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACtG,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,YAAY,EAAE,sBAAsB,EAAE,MAAM,0CAA0C,CAAC;AACvF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACtG,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,6 @@ export { RedisRelaySignal } from './relay/redis-relay-signal.js';
|
|
|
10
10
|
export { relayWakeUpMiddleware } from './relay/relay-wake-up-middleware.js';
|
|
11
11
|
export { InMemoryLock, Lock } from '@ddd-ts-kit/kernel';
|
|
12
12
|
export { InMemoryRateLimiter, RateLimiter } from '@ddd-ts-kit/kernel';
|
|
13
|
+
export { RedisJobStore } from './jobs/redis-job-store.js';
|
|
14
|
+
export { reportLostJobs } from './jobs/report-lost-jobs.js';
|
|
13
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,wBAAwB,CAAC;AAG9F,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAwB,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEtD,OAAO,EAAE,WAAW,EAA6C,MAAM,8BAA8B,CAAC;AAEtG,OAAO,EAAE,SAAS,EAAyB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,sCAAsC,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAgC,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAA6D,MAAM,oBAAoB,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAA0C,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4C,MAAM,wBAAwB,CAAC;AAG9F,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAwB,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEtD,OAAO,EAAE,WAAW,EAA6C,MAAM,8BAA8B,CAAC;AAEtG,OAAO,EAAE,SAAS,EAAyB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,sCAAsC,CAAC;AACtG,OAAO,EAAE,gBAAgB,EAAgC,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,IAAI,EAA6D,MAAM,oBAAoB,CAAC;AACnH,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAA0C,MAAM,oBAAoB,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ClaimedJob } from '@ddd-ts-kit/scheduling';
|
|
2
|
+
import type { RedisJobRecord } from './redis-job-record.js';
|
|
3
|
+
/** A record as the worker receives it. */
|
|
4
|
+
export declare function claimedJobOfRecord(record: RedisJobRecord): ClaimedJob;
|
|
5
|
+
//# sourceMappingURL=claimed-job-of-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claimed-job-of-record.d.ts","sourceRoot":"","sources":["../../src/jobs/claimed-job-of-record.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,0CAA0C;AAC1C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAarE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { parseEnqueuedBy } from './parse-enqueued-by.js';
|
|
2
|
+
/** A record as the worker receives it. */
|
|
3
|
+
export function claimedJobOfRecord(record) {
|
|
4
|
+
const payload = JSON.parse(record.payload);
|
|
5
|
+
return {
|
|
6
|
+
id: record.id,
|
|
7
|
+
name: record.name,
|
|
8
|
+
queue: record.queue,
|
|
9
|
+
payload,
|
|
10
|
+
enqueuedBy: parseEnqueuedBy(record.enqueuedBy),
|
|
11
|
+
enqueuedAt: new Date(record.enqueuedAt),
|
|
12
|
+
attempt: record.attempts,
|
|
13
|
+
...checkpointOf(record),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function checkpointOf(record) {
|
|
17
|
+
if (record.checkpoint === undefined) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const checkpoint = JSON.parse(record.checkpoint);
|
|
21
|
+
return { checkpoint };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=claimed-job-of-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claimed-job-of-record.js","sourceRoot":"","sources":["../../src/jobs/claimed-job-of-record.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,0CAA0C;AAC1C,MAAM,UAAU,kBAAkB,CAAC,MAAsB;IACvD,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEpD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO;QACP,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC;QAC9C,UAAU,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QACvC,OAAO,EAAE,MAAM,CAAC,QAAQ;QACxB,GAAG,YAAY,CAAC,MAAM,CAAC;KACxB,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,MAAsB;IAC1C,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,UAAU,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAE1D,OAAO,EAAE,UAAU,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** KEYS: records, waiting, keys. ARGV: id, record, idempotency field or '', run at. Answers { id, duplicate }. */
|
|
2
|
+
export declare const ADD_JOB = "\nif ARGV[3] ~= '' then\n local kept = redis.call('HGET', KEYS[3], ARGV[3])\n if kept then return {kept, 1} end\nend\nif redis.call('HEXISTS', KEYS[1], ARGV[1]) == 1 then return {ARGV[1], 0} end\nlocal record = cjson.decode(ARGV[2])\nrecord.seq = redis.call('HINCRBY', KEYS[1], '#sequence', 1)\nredis.call('HSET', KEYS[1], ARGV[1], cjson.encode(record))\nredis.call('ZADD', KEYS[2], ARGV[4], ARGV[1])\nif ARGV[3] ~= '' then redis.call('HSET', KEYS[3], ARGV[3], ARGV[1]) end\nreturn {ARGV[1], 0}";
|
|
3
|
+
/** KEYS: records, waiting, running. ARGV: now, limit, lease until. Answers the claimed records, earliest first. */
|
|
4
|
+
export declare const CLAIM_JOBS = "\nlocal limit = tonumber(ARGV[2])\nlocal records = {}\nfor _, set in ipairs({KEYS[2], KEYS[3]}) do\n for _, id in ipairs(redis.call('ZRANGEBYSCORE', set, '-inf', ARGV[1], 'LIMIT', 0, limit)) do\n local raw = redis.call('HGET', KEYS[1], id)\n if raw then table.insert(records, cjson.decode(raw)) end\n end\nend\ntable.sort(records, function(a, b)\n if a.runAt == b.runAt then return a.seq < b.seq end\n return a.runAt < b.runAt\nend)\nlocal claimed = {}\nfor index = 1, math.min(#records, limit) do\n local record = records[index]\n record.status = 'running'\n record.attempts = record.attempts + 1\n record.leaseUntil = tonumber(ARGV[3])\n local raw = cjson.encode(record)\n redis.call('HSET', KEYS[1], record.id, raw)\n redis.call('ZREM', KEYS[2], record.id)\n redis.call('ZADD', KEYS[3], ARGV[3], record.id)\n table.insert(claimed, raw)\nend\nreturn claimed";
|
|
5
|
+
/**
|
|
6
|
+
* KEYS: records, waiting, running, dead, finished. ARGV: id, statuses allowed (space separated),
|
|
7
|
+
* attempt or '', change. A change sets fields, removes those it sets to false, and adds
|
|
8
|
+
* `attemptsDelta` to the attempts. Answers 1 when it applied, 0 otherwise.
|
|
9
|
+
*/
|
|
10
|
+
export declare const TRANSITION_JOB = "\nlocal raw = redis.call('HGET', KEYS[1], ARGV[1])\nif not raw then return 0 end\nlocal record = cjson.decode(raw)\nif not string.find(' ' .. ARGV[2] .. ' ', ' ' .. record.status .. ' ', 1, true) then return 0 end\nif ARGV[3] ~= '' and record.attempts ~= tonumber(ARGV[3]) then return 0 end\nfor field, value in pairs(cjson.decode(ARGV[4])) do\n if field == 'attemptsDelta' then record.attempts = record.attempts + value\n elseif value == false then record[field] = nil\n else record[field] = value end\nend\nredis.call('HSET', KEYS[1], ARGV[1], cjson.encode(record))\nfor index = 2, 5 do redis.call('ZREM', KEYS[index], ARGV[1]) end\nif record.status == 'waiting' then redis.call('ZADD', KEYS[2], record.runAt, ARGV[1])\nelseif record.status == 'running' then redis.call('ZADD', KEYS[3], record.leaseUntil, ARGV[1])\nelseif record.status == 'dead' then redis.call('ZADD', KEYS[4], record.finishedAt, ARGV[1])\nelse redis.call('ZADD', KEYS[5], record.finishedAt, ARGV[1]) end\nreturn 1";
|
|
11
|
+
/** KEYS: records, finished, keys. ARGV: before. Answers how many records it dropped. */
|
|
12
|
+
export declare const PURGE_JOBS = "\nlocal ids = redis.call('ZRANGEBYSCORE', KEYS[2], '-inf', '(' .. ARGV[1])\nfor _, id in ipairs(ids) do\n local raw = redis.call('HGET', KEYS[1], id)\n if raw then\n local record = cjson.decode(raw)\n if record.key then redis.call('HDEL', KEYS[3], record.key) end\n redis.call('HDEL', KEYS[1], id)\n end\n redis.call('ZREM', KEYS[2], id)\nend\nreturn #ids";
|
|
13
|
+
//# sourceMappingURL=job-scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-scripts.d.ts","sourceRoot":"","sources":["../../src/jobs/job-scripts.ts"],"names":[],"mappings":"AAQA,kHAAkH;AAClH,eAAO,MAAM,OAAO,qfAWA,CAAC;AAErB,mHAAmH;AACnH,eAAO,MAAM,UAAU,m3BAyBR,CAAC;AAEhB;;;;GAIG;AACH,eAAO,MAAM,cAAc,i+BAiBlB,CAAC;AAEV,wFAAwF;AACxF,eAAO,MAAM,UAAU,qXAWX,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The scripts of the job store. Each one runs atomically on the server and touches only the keys
|
|
3
|
+
* it is given, which Dragonfly requires: the records hash, the sorted sets of one queue
|
|
4
|
+
* (`waiting` by due instant, `running` by end of lease, `dead`), the finished set and the
|
|
5
|
+
* idempotency keys. A record is JSON whose payload, checkpoint and progress are JSON texts the
|
|
6
|
+
* scripts never open.
|
|
7
|
+
*/
|
|
8
|
+
/** KEYS: records, waiting, keys. ARGV: id, record, idempotency field or '', run at. Answers { id, duplicate }. */
|
|
9
|
+
export const ADD_JOB = `
|
|
10
|
+
if ARGV[3] ~= '' then
|
|
11
|
+
local kept = redis.call('HGET', KEYS[3], ARGV[3])
|
|
12
|
+
if kept then return {kept, 1} end
|
|
13
|
+
end
|
|
14
|
+
if redis.call('HEXISTS', KEYS[1], ARGV[1]) == 1 then return {ARGV[1], 0} end
|
|
15
|
+
local record = cjson.decode(ARGV[2])
|
|
16
|
+
record.seq = redis.call('HINCRBY', KEYS[1], '#sequence', 1)
|
|
17
|
+
redis.call('HSET', KEYS[1], ARGV[1], cjson.encode(record))
|
|
18
|
+
redis.call('ZADD', KEYS[2], ARGV[4], ARGV[1])
|
|
19
|
+
if ARGV[3] ~= '' then redis.call('HSET', KEYS[3], ARGV[3], ARGV[1]) end
|
|
20
|
+
return {ARGV[1], 0}`;
|
|
21
|
+
/** KEYS: records, waiting, running. ARGV: now, limit, lease until. Answers the claimed records, earliest first. */
|
|
22
|
+
export const CLAIM_JOBS = `
|
|
23
|
+
local limit = tonumber(ARGV[2])
|
|
24
|
+
local records = {}
|
|
25
|
+
for _, set in ipairs({KEYS[2], KEYS[3]}) do
|
|
26
|
+
for _, id in ipairs(redis.call('ZRANGEBYSCORE', set, '-inf', ARGV[1], 'LIMIT', 0, limit)) do
|
|
27
|
+
local raw = redis.call('HGET', KEYS[1], id)
|
|
28
|
+
if raw then table.insert(records, cjson.decode(raw)) end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
table.sort(records, function(a, b)
|
|
32
|
+
if a.runAt == b.runAt then return a.seq < b.seq end
|
|
33
|
+
return a.runAt < b.runAt
|
|
34
|
+
end)
|
|
35
|
+
local claimed = {}
|
|
36
|
+
for index = 1, math.min(#records, limit) do
|
|
37
|
+
local record = records[index]
|
|
38
|
+
record.status = 'running'
|
|
39
|
+
record.attempts = record.attempts + 1
|
|
40
|
+
record.leaseUntil = tonumber(ARGV[3])
|
|
41
|
+
local raw = cjson.encode(record)
|
|
42
|
+
redis.call('HSET', KEYS[1], record.id, raw)
|
|
43
|
+
redis.call('ZREM', KEYS[2], record.id)
|
|
44
|
+
redis.call('ZADD', KEYS[3], ARGV[3], record.id)
|
|
45
|
+
table.insert(claimed, raw)
|
|
46
|
+
end
|
|
47
|
+
return claimed`;
|
|
48
|
+
/**
|
|
49
|
+
* KEYS: records, waiting, running, dead, finished. ARGV: id, statuses allowed (space separated),
|
|
50
|
+
* attempt or '', change. A change sets fields, removes those it sets to false, and adds
|
|
51
|
+
* `attemptsDelta` to the attempts. Answers 1 when it applied, 0 otherwise.
|
|
52
|
+
*/
|
|
53
|
+
export const TRANSITION_JOB = `
|
|
54
|
+
local raw = redis.call('HGET', KEYS[1], ARGV[1])
|
|
55
|
+
if not raw then return 0 end
|
|
56
|
+
local record = cjson.decode(raw)
|
|
57
|
+
if not string.find(' ' .. ARGV[2] .. ' ', ' ' .. record.status .. ' ', 1, true) then return 0 end
|
|
58
|
+
if ARGV[3] ~= '' and record.attempts ~= tonumber(ARGV[3]) then return 0 end
|
|
59
|
+
for field, value in pairs(cjson.decode(ARGV[4])) do
|
|
60
|
+
if field == 'attemptsDelta' then record.attempts = record.attempts + value
|
|
61
|
+
elseif value == false then record[field] = nil
|
|
62
|
+
else record[field] = value end
|
|
63
|
+
end
|
|
64
|
+
redis.call('HSET', KEYS[1], ARGV[1], cjson.encode(record))
|
|
65
|
+
for index = 2, 5 do redis.call('ZREM', KEYS[index], ARGV[1]) end
|
|
66
|
+
if record.status == 'waiting' then redis.call('ZADD', KEYS[2], record.runAt, ARGV[1])
|
|
67
|
+
elseif record.status == 'running' then redis.call('ZADD', KEYS[3], record.leaseUntil, ARGV[1])
|
|
68
|
+
elseif record.status == 'dead' then redis.call('ZADD', KEYS[4], record.finishedAt, ARGV[1])
|
|
69
|
+
else redis.call('ZADD', KEYS[5], record.finishedAt, ARGV[1]) end
|
|
70
|
+
return 1`;
|
|
71
|
+
/** KEYS: records, finished, keys. ARGV: before. Answers how many records it dropped. */
|
|
72
|
+
export const PURGE_JOBS = `
|
|
73
|
+
local ids = redis.call('ZRANGEBYSCORE', KEYS[2], '-inf', '(' .. ARGV[1])
|
|
74
|
+
for _, id in ipairs(ids) do
|
|
75
|
+
local raw = redis.call('HGET', KEYS[1], id)
|
|
76
|
+
if raw then
|
|
77
|
+
local record = cjson.decode(raw)
|
|
78
|
+
if record.key then redis.call('HDEL', KEYS[3], record.key) end
|
|
79
|
+
redis.call('HDEL', KEYS[1], id)
|
|
80
|
+
end
|
|
81
|
+
redis.call('ZREM', KEYS[2], id)
|
|
82
|
+
end
|
|
83
|
+
return #ids`;
|
|
84
|
+
//# sourceMappingURL=job-scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-scripts.js","sourceRoot":"","sources":["../../src/jobs/job-scripts.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,kHAAkH;AAClH,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;oBAWH,CAAC;AAErB,mHAAmH;AACnH,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;eAyBX,CAAC;AAEhB;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;SAiBrB,CAAC;AAEV,wFAAwF;AACxF,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;YAWd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-enqueued-by.d.ts","sourceRoot":"","sources":["../../src/jobs/parse-enqueued-by.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE9D,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAoBvD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isActor } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/** Reads who enqueued a job, as the store wrote it. */
|
|
3
|
+
export function parseEnqueuedBy(raw) {
|
|
4
|
+
const parsed = JSON.parse(raw);
|
|
5
|
+
const actor = typeof parsed === 'object' && parsed !== null ? Reflect.get(parsed, 'actor') : undefined;
|
|
6
|
+
const correlationId = typeof parsed === 'object' && parsed !== null ? Reflect.get(parsed, 'correlationId') : undefined;
|
|
7
|
+
if (!isActor(actor) || typeof correlationId !== 'string') {
|
|
8
|
+
throw new TypeError('Who enqueued a background job of Redis is not written the way the store writes it.');
|
|
9
|
+
}
|
|
10
|
+
const tenantId = textOf(parsed, 'tenantId');
|
|
11
|
+
const traceparent = textOf(parsed, 'traceparent');
|
|
12
|
+
return {
|
|
13
|
+
correlationId,
|
|
14
|
+
actor,
|
|
15
|
+
...(tenantId === undefined ? {} : { tenantId }),
|
|
16
|
+
...(traceparent === undefined ? {} : { traceparent }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function textOf(value, field) {
|
|
20
|
+
const text = typeof value === 'object' && value !== null ? Reflect.get(value, field) : undefined;
|
|
21
|
+
return typeof text === 'string' ? text : undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=parse-enqueued-by.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-enqueued-by.js","sourceRoot":"","sources":["../../src/jobs/parse-enqueued-by.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAmB,MAAM,oBAAoB,CAAC;AAE9D,uDAAuD;AACvD,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,KAAK,GAAY,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEhH,MAAM,aAAa,GACjB,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,oFAAoF,CAAC,CAAC;IAC5G,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAElD,OAAO;QACL,aAAa;QACb,KAAK;QACL,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;QAC/C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,KAAc,EAAE,KAAa;IAC3C,MAAM,IAAI,GAAY,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE1G,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RedisJobRecord } from './redis-job-record.js';
|
|
2
|
+
/** Reads a record the scripts wrote. Throws on anything else: the keys of the namespace belong to the store. */
|
|
3
|
+
export declare function parseJobRecord(raw: string): RedisJobRecord;
|
|
4
|
+
//# sourceMappingURL=parse-job-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-job-record.d.ts","sourceRoot":"","sources":["../../src/jobs/parse-job-record.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,gHAAgH;AAChH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAQ1D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Reads a record the scripts wrote. Throws on anything else: the keys of the namespace belong to the store. */
|
|
2
|
+
export function parseJobRecord(raw) {
|
|
3
|
+
const parsed = JSON.parse(raw);
|
|
4
|
+
if (!isJobRecord(parsed)) {
|
|
5
|
+
throw new TypeError('A background job record of Redis does not have the shape the store writes.');
|
|
6
|
+
}
|
|
7
|
+
return parsed;
|
|
8
|
+
}
|
|
9
|
+
function isJobRecord(value) {
|
|
10
|
+
if (typeof value !== 'object' || value === null) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const fields = ['id', 'name', 'queue', 'payload', 'enqueuedBy', 'status'].map((field) => Reflect.get(value, field));
|
|
14
|
+
const instants = ['attempts', 'runAt', 'enqueuedAt', 'seq'].map((field) => Reflect.get(value, field));
|
|
15
|
+
return fields.every((field) => typeof field === 'string') && instants.every((field) => typeof field === 'number');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=parse-job-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-job-record.js","sourceRoot":"","sources":["../../src/jobs/parse-job-record.ts"],"names":[],"mappings":"AAEA,gHAAgH;AAChH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAExC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CAAC,4EAA4E,CAAC,CAAC;IACpG,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAW,EAAE,CAC/F,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAC1B,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IAE/G,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;AACpH,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { QueuedJob } from '@ddd-ts-kit/kernel';
|
|
2
|
+
import type { RedisJobRecord } from './redis-job-record.js';
|
|
3
|
+
/** What a record shows of its job: never the payload, the checkpoint or the actor. */
|
|
4
|
+
export declare function queuedJobOfRecord(record: RedisJobRecord): QueuedJob;
|
|
5
|
+
//# sourceMappingURL=queued-job-of-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queued-job-of-record.d.ts","sourceRoot":"","sources":["../../src/jobs/queued-job-of-record.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CAenE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** What a record shows of its job: never the payload, the checkpoint or the actor. */
|
|
2
|
+
export function queuedJobOfRecord(record) {
|
|
3
|
+
return {
|
|
4
|
+
id: record.id,
|
|
5
|
+
name: record.name,
|
|
6
|
+
queue: record.queue,
|
|
7
|
+
status: record.status,
|
|
8
|
+
attempts: record.attempts,
|
|
9
|
+
enqueuedAt: new Date(record.enqueuedAt),
|
|
10
|
+
runAt: new Date(record.runAt),
|
|
11
|
+
...(record.tenantId === undefined ? {} : { tenantId: record.tenantId }),
|
|
12
|
+
...(record.subject === undefined ? {} : { subject: record.subject }),
|
|
13
|
+
...(record.progress === undefined ? {} : { progress: record.progress }),
|
|
14
|
+
...(record.lastErrorCode === undefined ? {} : { lastErrorCode: record.lastErrorCode }),
|
|
15
|
+
...(record.finishedAt === undefined ? {} : { finishedAt: new Date(record.finishedAt) }),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=queued-job-of-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queued-job-of-record.js","sourceRoot":"","sources":["../../src/jobs/queued-job-of-record.ts"],"names":[],"mappings":"AAGA,sFAAsF;AACtF,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACtD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QACvC,KAAK,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvE,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACpE,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvE,GAAG,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;QACtF,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;KACxF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { JobProgress, QueuedJobStatus } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/** A job as the Redis store keeps it: instants in epoch milliseconds, JSON values as texts. */
|
|
3
|
+
export interface RedisJobRecord {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly queue: string;
|
|
7
|
+
readonly payload: string;
|
|
8
|
+
/** A JSON text: an empty list would come back from the scripts as an empty object. */
|
|
9
|
+
readonly enqueuedBy: string;
|
|
10
|
+
/** The idempotency field: name, tenant and key together. */
|
|
11
|
+
readonly key?: string;
|
|
12
|
+
readonly tenantId?: string;
|
|
13
|
+
readonly subject?: string;
|
|
14
|
+
readonly seq: number;
|
|
15
|
+
readonly status: QueuedJobStatus;
|
|
16
|
+
readonly attempts: number;
|
|
17
|
+
readonly runAt: number;
|
|
18
|
+
readonly leaseUntil?: number;
|
|
19
|
+
readonly progress?: JobProgress;
|
|
20
|
+
readonly checkpoint?: string;
|
|
21
|
+
readonly lastErrorCode?: string;
|
|
22
|
+
readonly enqueuedAt: number;
|
|
23
|
+
readonly finishedAt?: number;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=redis-job-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-record.d.ts","sourceRoot":"","sources":["../../src/jobs/redis-job-record.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEvE,+FAA+F;AAC/F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sFAAsF;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-record.js","sourceRoot":"","sources":["../../src/jobs/redis-job-record.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface RedisJobStoreOptions {
|
|
2
|
+
/** Prefix of the keys, typically the application. Default `jobs`. */
|
|
3
|
+
readonly namespace?: string;
|
|
4
|
+
/**
|
|
5
|
+
* Told when a job staged by a committed unit of work could not be written (that job is lost),
|
|
6
|
+
* or a cancellation or a revival staged the same way could not be applied. The composition
|
|
7
|
+
* root hands it to the error reporter and the logger. Default: nothing is told.
|
|
8
|
+
*/
|
|
9
|
+
readonly onLostJob?: (error: unknown, jobId: string) => void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=redis-job-store-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-store-options.d.ts","sourceRoot":"","sources":["../../src/jobs/redis-job-store-options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-store-options.js","sourceRoot":"","sources":["../../src/jobs/redis-job-store-options.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import type { Redis } from 'ioredis';
|
|
3
|
+
import { type EnqueuedJob, type QueuedJob } from '@ddd-ts-kit/kernel';
|
|
4
|
+
import { type ClaimedJob, type ClaimRequest, type JobFailure, type JobLease, type JobStore, type JobStoreStats, type JobUpdate, type NewJob } from '@ddd-ts-kit/scheduling';
|
|
5
|
+
import type { RedisJobStoreOptions } from './redis-job-store-options.js';
|
|
6
|
+
/**
|
|
7
|
+
* The background jobs on Redis, Dragonfly or Valkey, in a few Lua scripts: no dependency but the
|
|
8
|
+
* client of the package. Redis cannot join the transaction of the database: inside a unit of
|
|
9
|
+
* work, `add`, `cancel` and `retryDead` answer from what Redis holds and write once it committed
|
|
10
|
+
* (`whenCommitted`), so a command that rolls back leaves no job and cancels none, and a crash
|
|
11
|
+
* between the commit and the write loses that write. Every key carries the namespace in braces,
|
|
12
|
+
* so that a cluster keeps them in one slot.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RedisJobStore implements JobStore {
|
|
15
|
+
private readonly client;
|
|
16
|
+
private readonly prefix;
|
|
17
|
+
private readonly onLostJob;
|
|
18
|
+
constructor(client: Redis, options?: RedisJobStoreOptions);
|
|
19
|
+
add(job: NewJob): Promise<EnqueuedJob>;
|
|
20
|
+
claim({ queue, limit, now, leaseMs }: ClaimRequest): Promise<ClaimedJob[]>;
|
|
21
|
+
heartbeat(lease: JobLease, leaseUntil: Date): Promise<boolean>;
|
|
22
|
+
update(lease: JobLease, update: JobUpdate, leaseUntil: Date): Promise<boolean>;
|
|
23
|
+
complete(lease: JobLease, at: Date): Promise<boolean>;
|
|
24
|
+
fail(lease: JobLease, { errorCode, retryAt, at }: JobFailure): Promise<boolean>;
|
|
25
|
+
release(lease: JobLease, runAt: Date): Promise<boolean>;
|
|
26
|
+
cancel(id: string, at: Date): Promise<boolean>;
|
|
27
|
+
find(id: string): Promise<QueuedJob | undefined>;
|
|
28
|
+
retryDead(ids: readonly string[], now: Date): Promise<number>;
|
|
29
|
+
purgeFinished(before: Date): Promise<number>;
|
|
30
|
+
stats(queue: string): Promise<JobStoreStats>;
|
|
31
|
+
/** What a serialisation shows: a name, never the client, whose options carry the password. */
|
|
32
|
+
toJSON(): string;
|
|
33
|
+
/** What `console.log` and `util.inspect` show: the same. */
|
|
34
|
+
[inspect.custom](): string;
|
|
35
|
+
private write;
|
|
36
|
+
/**
|
|
37
|
+
* Inside a unit of work, `write` waits for the commit and `expected` is the answer; anywhere
|
|
38
|
+
* else, it runs at once and its own answer is.
|
|
39
|
+
*/
|
|
40
|
+
private afterCommit;
|
|
41
|
+
private revive;
|
|
42
|
+
private holding;
|
|
43
|
+
/** The queue of a job never changes: it is read first, to name the keys the script may touch. */
|
|
44
|
+
private transition;
|
|
45
|
+
private record;
|
|
46
|
+
private key;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=redis-job-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-store.d.ts","sourceRoot":"","sources":["../../src/jobs/redis-job-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAiB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,MAAM,EACZ,MAAM,wBAAwB,CAAC;AAQhC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAgBzE;;;;;;;GAOG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAE/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IAEpE,YAAY,MAAM,EAAE,KAAK,EAAE,OAAO,GAAE,oBAAyB,EAK5D;IAEK,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAoB3C;IAEK,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAY/E;IAED,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAE7D;IAED,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAM7E;IAED,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpD;IAED,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAK9E;IAED,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAEtD;IAEK,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAUnD;IAEK,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAIrD;IAEK,SAAS,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlE;IAEK,aAAa,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAIjD;IAEK,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAMjD;IAED,8FAA8F;IAC9F,MAAM,IAAI,MAAM,CAEf;IAED,4DAA4D;IAC5D,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAEzB;YAEa,KAAK;IAsBnB;;;OAGG;YACW,WAAW;YAiBX,MAAM;IAYpB,OAAO,CAAC,OAAO;IAIf,iGAAiG;YACnF,UAAU;YA4BV,MAAM;IAMpB,OAAO,CAAC,GAAG;CAGZ"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { inspect } from 'node:util';
|
|
2
|
+
import { whenCommitted } from '@ddd-ts-kit/kernel';
|
|
3
|
+
import { tenantKeyOf, } from '@ddd-ts-kit/scheduling';
|
|
4
|
+
import { listOf } from '../replies/list-of.js';
|
|
5
|
+
import { conceal } from '../secrecy/conceal.js';
|
|
6
|
+
import { claimedJobOfRecord } from './claimed-job-of-record.js';
|
|
7
|
+
import { ADD_JOB, CLAIM_JOBS, PURGE_JOBS, TRANSITION_JOB } from './job-scripts.js';
|
|
8
|
+
import { parseJobRecord } from './parse-job-record.js';
|
|
9
|
+
import { queuedJobOfRecord } from './queued-job-of-record.js';
|
|
10
|
+
const DESCRIPTION = 'RedisJobStore [client concealed]';
|
|
11
|
+
/**
|
|
12
|
+
* The background jobs on Redis, Dragonfly or Valkey, in a few Lua scripts: no dependency but the
|
|
13
|
+
* client of the package. Redis cannot join the transaction of the database: inside a unit of
|
|
14
|
+
* work, `add`, `cancel` and `retryDead` answer from what Redis holds and write once it committed
|
|
15
|
+
* (`whenCommitted`), so a command that rolls back leaves no job and cancels none, and a crash
|
|
16
|
+
* between the commit and the write loses that write. Every key carries the namespace in braces,
|
|
17
|
+
* so that a cluster keeps them in one slot.
|
|
18
|
+
*/
|
|
19
|
+
export class RedisJobStore {
|
|
20
|
+
client;
|
|
21
|
+
prefix;
|
|
22
|
+
onLostJob;
|
|
23
|
+
constructor(client, options = {}) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
this.prefix = `{${options.namespace ?? 'jobs'}}`;
|
|
26
|
+
this.onLostJob = options.onLostJob ?? (() => undefined);
|
|
27
|
+
conceal(this);
|
|
28
|
+
}
|
|
29
|
+
async add(job) {
|
|
30
|
+
const field = job.idempotencyKey === undefined
|
|
31
|
+
? ''
|
|
32
|
+
: JSON.stringify([job.name, tenantKeyOf(job.enqueuedBy), job.idempotencyKey]);
|
|
33
|
+
const kept = field === '' ? null : await this.client.hget(this.key('keys'), field);
|
|
34
|
+
if (kept !== null) {
|
|
35
|
+
return { id: kept, duplicate: true };
|
|
36
|
+
}
|
|
37
|
+
await whenCommitted(() => this.write(job, field), (error) => {
|
|
38
|
+
this.onLostJob(error, job.id);
|
|
39
|
+
});
|
|
40
|
+
return { id: job.id, duplicate: false };
|
|
41
|
+
}
|
|
42
|
+
async claim({ queue, limit, now, leaseMs }) {
|
|
43
|
+
if (limit <= 0) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const keys = [this.key('records'), this.key('waiting', queue), this.key('running', queue)];
|
|
47
|
+
const leaseUntil = now.getTime() + leaseMs;
|
|
48
|
+
const claimed = await this.client.eval(CLAIM_JOBS, keys.length, ...keys, now.getTime(), limit, leaseUntil);
|
|
49
|
+
return listOf(claimed)
|
|
50
|
+
.filter((raw) => typeof raw === 'string')
|
|
51
|
+
.map((raw) => claimedJobOfRecord(parseJobRecord(raw)));
|
|
52
|
+
}
|
|
53
|
+
heartbeat(lease, leaseUntil) {
|
|
54
|
+
return this.holding(lease, { leaseUntil: leaseUntil.getTime() });
|
|
55
|
+
}
|
|
56
|
+
update(lease, update, leaseUntil) {
|
|
57
|
+
return this.holding(lease, {
|
|
58
|
+
leaseUntil: leaseUntil.getTime(),
|
|
59
|
+
...(update.progress === undefined ? {} : { progress: update.progress }),
|
|
60
|
+
...('checkpoint' in update ? { checkpoint: jsonOrFalse(update.checkpoint) } : {}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
complete(lease, at) {
|
|
64
|
+
return this.holding(lease, { status: 'succeeded', finishedAt: at.getTime(), leaseUntil: false });
|
|
65
|
+
}
|
|
66
|
+
fail(lease, { errorCode, retryAt, at }) {
|
|
67
|
+
const next = retryAt === null ? { status: 'dead', finishedAt: at.getTime() } : { status: 'waiting', runAt: retryAt.getTime() };
|
|
68
|
+
return this.holding(lease, { ...next, leaseUntil: false, lastErrorCode: errorCode });
|
|
69
|
+
}
|
|
70
|
+
release(lease, runAt) {
|
|
71
|
+
return this.holding(lease, { status: 'waiting', runAt: runAt.getTime(), leaseUntil: false, attemptsDelta: -1 });
|
|
72
|
+
}
|
|
73
|
+
async cancel(id, at) {
|
|
74
|
+
const status = (await this.record(id))?.status;
|
|
75
|
+
if (status !== 'waiting' && status !== 'running') {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const change = { status: 'cancelled', finishedAt: at.getTime(), leaseUntil: false };
|
|
79
|
+
return this.afterCommit(true, [id], () => this.transition({ id, from: 'waiting running', change }));
|
|
80
|
+
}
|
|
81
|
+
async find(id) {
|
|
82
|
+
const record = await this.record(id);
|
|
83
|
+
return record === undefined ? undefined : queuedJobOfRecord(record);
|
|
84
|
+
}
|
|
85
|
+
async retryDead(ids, now) {
|
|
86
|
+
const records = await Promise.all(ids.map((id) => this.record(id)));
|
|
87
|
+
const dead = records.flatMap((record) => (record?.status === 'dead' ? [record.id] : []));
|
|
88
|
+
return this.afterCommit(dead.length, dead, () => this.revive(dead, now));
|
|
89
|
+
}
|
|
90
|
+
async purgeFinished(before) {
|
|
91
|
+
const keys = [this.key('records'), this.key('finished'), this.key('keys')];
|
|
92
|
+
return Number(await this.client.eval(PURGE_JOBS, keys.length, ...keys, before.getTime()));
|
|
93
|
+
}
|
|
94
|
+
async stats(queue) {
|
|
95
|
+
const [waiting, running, dead] = await Promise.all(['waiting', 'running', 'dead'].map((set) => this.client.zcard(this.key(set, queue))));
|
|
96
|
+
return { waiting: waiting ?? 0, running: running ?? 0, dead: dead ?? 0 };
|
|
97
|
+
}
|
|
98
|
+
/** What a serialisation shows: a name, never the client, whose options carry the password. */
|
|
99
|
+
toJSON() {
|
|
100
|
+
return DESCRIPTION;
|
|
101
|
+
}
|
|
102
|
+
/** What `console.log` and `util.inspect` show: the same. */
|
|
103
|
+
[inspect.custom]() {
|
|
104
|
+
return DESCRIPTION;
|
|
105
|
+
}
|
|
106
|
+
async write(job, field) {
|
|
107
|
+
const keys = [this.key('records'), this.key('waiting', job.queue), this.key('keys')];
|
|
108
|
+
const runAt = job.runAt.getTime();
|
|
109
|
+
const record = {
|
|
110
|
+
id: job.id,
|
|
111
|
+
name: job.name,
|
|
112
|
+
queue: job.queue,
|
|
113
|
+
payload: jsonOrFalse(job.payload) || 'null',
|
|
114
|
+
enqueuedBy: JSON.stringify(job.enqueuedBy),
|
|
115
|
+
...(field === '' ? {} : { key: field }),
|
|
116
|
+
...(job.enqueuedBy.tenantId === undefined ? {} : { tenantId: job.enqueuedBy.tenantId }),
|
|
117
|
+
...(job.subject === undefined ? {} : { subject: job.subject }),
|
|
118
|
+
status: 'waiting',
|
|
119
|
+
attempts: 0,
|
|
120
|
+
runAt,
|
|
121
|
+
enqueuedAt: job.enqueuedAt.getTime(),
|
|
122
|
+
};
|
|
123
|
+
await this.client.eval(ADD_JOB, keys.length, ...keys, job.id, JSON.stringify(record), field, runAt);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Inside a unit of work, `write` waits for the commit and `expected` is the answer; anywhere
|
|
127
|
+
* else, it runs at once and its own answer is.
|
|
128
|
+
*/
|
|
129
|
+
async afterCommit(expected, ids, write) {
|
|
130
|
+
let answer = expected;
|
|
131
|
+
await whenCommitted(async () => {
|
|
132
|
+
answer = await write();
|
|
133
|
+
}, (error) => {
|
|
134
|
+
for (const id of ids) {
|
|
135
|
+
this.onLostJob(error, id);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
return answer;
|
|
139
|
+
}
|
|
140
|
+
async revive(ids, now) {
|
|
141
|
+
let revived = 0;
|
|
142
|
+
for (const id of ids) {
|
|
143
|
+
const change = { status: 'waiting', runAt: now.getTime(), finishedAt: false, attempts: 0 };
|
|
144
|
+
revived += (await this.transition({ id, from: 'dead', change })) ? 1 : 0;
|
|
145
|
+
}
|
|
146
|
+
return revived;
|
|
147
|
+
}
|
|
148
|
+
holding(lease, change) {
|
|
149
|
+
return this.transition({ id: lease.id, from: 'running', attempt: lease.attempt, change });
|
|
150
|
+
}
|
|
151
|
+
/** The queue of a job never changes: it is read first, to name the keys the script may touch. */
|
|
152
|
+
async transition({ id, from, attempt, change }) {
|
|
153
|
+
const record = await this.record(id);
|
|
154
|
+
if (record === undefined) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
const keys = [
|
|
158
|
+
this.key('records'),
|
|
159
|
+
...['waiting', 'running', 'dead'].map((set) => this.key(set, record.queue)),
|
|
160
|
+
this.key('finished'),
|
|
161
|
+
];
|
|
162
|
+
const held = attempt === undefined ? '' : String(attempt);
|
|
163
|
+
const applied = await this.client.eval(TRANSITION_JOB, keys.length, ...keys, id, from, held, JSON.stringify(change));
|
|
164
|
+
return Number(applied) === 1;
|
|
165
|
+
}
|
|
166
|
+
async record(id) {
|
|
167
|
+
const raw = await this.client.hget(this.key('records'), id);
|
|
168
|
+
return raw === null ? undefined : parseJobRecord(raw);
|
|
169
|
+
}
|
|
170
|
+
key(...parts) {
|
|
171
|
+
return [this.prefix, ...parts].join(':');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/** JSON of a value, or `false` (the field is removed) for `undefined`, which JSON cannot write. */
|
|
175
|
+
function jsonOrFalse(value) {
|
|
176
|
+
return value === undefined ? false : JSON.stringify(value);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=redis-job-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-job-store.js","sourceRoot":"","sources":["../../src/jobs/redis-job-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,aAAa,EAAoC,MAAM,oBAAoB,CAAC;AACrF,OAAO,EACL,WAAW,GASZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAI9D,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAcvD;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IACP,MAAM,CAAQ;IAEd,MAAM,CAAS;IAEf,SAAS,CAA0C;IAEpE,YAAY,MAAa,EAAE,OAAO,GAAyB,EAAE;QAC3D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAC,SAAS,IAAI,MAAM,GAAG,CAAC;QACjD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,KAAK,GACT,GAAG,CAAC,cAAc,KAAK,SAAS;YAC9B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;QAElF,MAAM,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAEnF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACvC,CAAC;QAED,MAAM,aAAa,CACjB,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,EAC5B,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,CAAC,CACF,CAAC;QAEF,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAgB;QACtD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;QAC3F,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC;QAC3C,MAAM,OAAO,GAAY,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAEpH,OAAO,MAAM,CAAC,OAAO,CAAC;aACnB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC;aACxC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,SAAS,CAAC,KAAe,EAAE,UAAgB;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,CAAC,KAAe,EAAE,MAAiB,EAAE,UAAgB;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;YACzB,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE;YAChC,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;YACvE,GAAG,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClF,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,KAAe,EAAE,EAAQ;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;IACnG,CAAC;IAED,IAAI,CAAC,KAAe,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAc;QAC1D,MAAM,IAAI,GACR,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAEpH,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,OAAO,CAAC,KAAe,EAAE,KAAW;QAClC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAClH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,EAAQ;QAC/B,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAE/C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QAEpF,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACtG,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAU;QACnB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErC,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAsB,EAAE,GAAS;QAC/C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzF,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAAY;QAC9B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAE3E,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAa;QACvB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAChD,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CACrF,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,8FAA8F;IAC9F,MAAM;QACJ,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,4DAA4D;IAC5D,CAAC,OAAO,CAAC,MAAM,CAAC;QACd,OAAO,WAAW,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,KAAa;QAC5C,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACrF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAElC,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM;YAC3C,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;YAC1C,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;YACvC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YACvF,GAAG,CAAC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;YAC9D,MAAM,EAAE,SAAS;YACjB,QAAQ,EAAE,CAAC;YACX,KAAK;YACL,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE;SACrC,CAAC;QAEF,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACtG,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,WAAW,CAAI,QAAW,EAAE,GAAsB,EAAE,KAAuB;QACvF,IAAI,MAAM,GAAG,QAAQ,CAAC;QAEtB,MAAM,aAAa,CACjB,KAAK,IAAI,EAAE;YACT,MAAM,GAAG,MAAM,KAAK,EAAE,CAAC;QACzB,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;YACR,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,CACF,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,GAAsB,EAAE,GAAS;QACpD,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;YAE3F,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,OAAO,CAAC,KAAe,EAAE,MAAc;QAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,iGAAiG;IACzF,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAc;QAChE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAErC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,IAAI,GAAG;YACX,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;YACnB,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YAC3E,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;SACrB,CAAC;QAEF,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE1D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CACpC,cAAc,EACd,IAAI,CAAC,MAAM,EACX,GAAG,IAAI,EACP,EAAE,EACF,IAAI,EACJ,IAAI,EACJ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CACvB,CAAC;QAEF,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,EAAU;QAC7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5D,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAEO,GAAG,CAAC,GAAG,KAAwB;QACrC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;CACF;AAED,mGAAmG;AACnG,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ErrorReporter, type Logger } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The `onLostJob` of a `RedisJobStore` a composition root wires: a job a committed command
|
|
4
|
+
* enqueued, or a cancellation it decided, that Redis did not take is logged, and reported when a
|
|
5
|
+
* reporter is given: never dropped in silence.
|
|
6
|
+
*
|
|
7
|
+
* new RedisJobStore(client, { onLostJob: reportLostJobs(logger, errors) });
|
|
8
|
+
*/
|
|
9
|
+
export declare function reportLostJobs(logger: Logger, errors?: ErrorReporter): (error: unknown, jobId: string) => void;
|
|
10
|
+
//# sourceMappingURL=report-lost-jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-lost-jobs.d.ts","sourceRoot":"","sources":["../../src/jobs/report-lost-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,aAAa,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAY9G"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { errorReportOf, identifyError } from '@ddd-ts-kit/kernel';
|
|
2
|
+
/**
|
|
3
|
+
* The `onLostJob` of a `RedisJobStore` a composition root wires: a job a committed command
|
|
4
|
+
* enqueued, or a cancellation it decided, that Redis did not take is logged, and reported when a
|
|
5
|
+
* reporter is given: never dropped in silence.
|
|
6
|
+
*
|
|
7
|
+
* new RedisJobStore(client, { onLostJob: reportLostJobs(logger, errors) });
|
|
8
|
+
*/
|
|
9
|
+
export function reportLostJobs(logger, errors) {
|
|
10
|
+
return (error, jobId) => {
|
|
11
|
+
logger.error('A background job write was lost after its unit of work committed', {
|
|
12
|
+
jobId,
|
|
13
|
+
...identifyError(error),
|
|
14
|
+
});
|
|
15
|
+
errors?.report(error, errorReportOf(error, { origin: 'queue', operation: 'background job write', severity: 'error' }));
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=report-lost-jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-lost-jobs.js","sourceRoot":"","sources":["../../src/jobs/report-lost-jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAmC,MAAM,oBAAoB,CAAC;AAEnG;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAsB;IACnE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtB,MAAM,CAAC,KAAK,CAAC,kEAAkE,EAAE;YAC/E,KAAK;YACL,GAAG,aAAa,CAAC,KAAK,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,EAAE,MAAM,CACZ,KAAK,EACL,aAAa,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAChG,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddd-ts-kit/redis",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Adapters of the kernel ports on the Redis protocol (Redis, Dragonfly, Valkey): cache, distributed lock, rate limiter, relay wake-up signal, and an integration event provider on Redis Streams.",
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Adapters of the kernel ports on the Redis protocol (Redis, Dragonfly, Valkey): cache, distributed lock, rate limiter, relay wake-up signal, a background job store, and an integration event provider on Redis Streams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -34,11 +34,12 @@
|
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@ddd-ts-kit/kernel": "0.
|
|
37
|
+
"@ddd-ts-kit/kernel": "0.11.0",
|
|
38
|
+
"@ddd-ts-kit/scheduling": "0.11.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"ioredis": "^5.0.0 || ^6.0.0",
|
|
41
|
-
"@ddd-ts-kit/testing": "0.
|
|
42
|
+
"@ddd-ts-kit/testing": "0.11.0"
|
|
42
43
|
},
|
|
43
44
|
"peerDependenciesMeta": {
|
|
44
45
|
"@ddd-ts-kit/testing": {
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"ioredis": "^6.0.0",
|
|
50
|
-
"@ddd-ts-kit/events": "0.
|
|
51
|
-
"@ddd-ts-kit/testing": "0.
|
|
51
|
+
"@ddd-ts-kit/events": "0.11.0",
|
|
52
|
+
"@ddd-ts-kit/testing": "0.11.0"
|
|
52
53
|
}
|
|
53
54
|
}
|