@nest-native/jobs 0.3.0 → 0.5.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 +8 -5
- package/dist/dialects/mysql/job-store.d.ts +4 -4
- package/dist/dialects/mysql/job-store.js +26 -11
- package/dist/dialects/mysql/job-store.js.map +1 -1
- package/dist/dialects/postgres/job-store.d.ts +4 -4
- package/dist/dialects/postgres/job-store.js +34 -11
- package/dist/dialects/postgres/job-store.js.map +1 -1
- package/dist/dialects/postgres/schedule-store.js +2 -1
- package/dist/dialects/postgres/schedule-store.js.map +1 -1
- package/dist/dialects/postgres/transaction.d.ts +19 -0
- package/dist/dialects/postgres/transaction.js +110 -0
- package/dist/dialects/postgres/transaction.js.map +1 -0
- package/dist/dialects/sqlite/job-store.d.ts +4 -4
- package/dist/dialects/sqlite/job-store.js +18 -12
- package/dist/dialects/sqlite/job-store.js.map +1 -1
- package/dist/interfaces.d.ts +29 -4
- package/dist/jobs-claimer.service.d.ts +35 -0
- package/dist/jobs-claimer.service.js +108 -17
- package/dist/jobs-claimer.service.js.map +1 -1
- package/dist/jobs-worker.d.ts +5 -1
- package/dist/jobs-worker.js +6 -0
- package/dist/jobs-worker.js.map +1 -1
- package/dist/testing/harness.js +2 -1
- package/dist/testing/harness.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,8 +41,8 @@ npm install drizzle-orm @nestjs-cls/transactional better-sqlite3 # or pg / mys
|
|
|
41
41
|
| `@nestjs-cls/transactional` | `^3.0.0` | on NestJS 12 you need `>=3.3.0` (with `nestjs-cls >=6.3.0`) — earlier minors declare `@nestjs/core >= 10 < 12` |
|
|
42
42
|
| `drizzle-orm` | `^0.44.0 \|\| ^0.45.0` | |
|
|
43
43
|
| `better-sqlite3` | `^11.0.0 \|\| ^12.0.0 \|\| ^13.0.0` | optional; 13 requires Node `>=22` |
|
|
44
|
-
| `pg` | `^8.0.0` | optional |
|
|
45
|
-
| `mysql2` | `^3.0.0` | optional |
|
|
44
|
+
| `pg` | `^8.0.0` | optional; give the `Pool` an `error` listener, as node-postgres requires (the Postgres stores warn once without one) |
|
|
45
|
+
| `mysql2` | `^3.0.0` | optional; needs MySQL 8.0.1+ (for `SKIP LOCKED`), with `binlog_format` ROW or MIXED when binary logging is on |
|
|
46
46
|
|
|
47
47
|
The Node.js floor depends on which end of the NestJS range you are on. NestJS
|
|
48
48
|
11 runs on any Node.js `>=22`. NestJS 12 is ESM-only; a CommonJS app — the
|
|
@@ -50,7 +50,9 @@ usual NestJS build, and this package itself — loads it through Node's
|
|
|
50
50
|
`require(esm)`, which is behind a flag before Node.js 22.12.0, so NestJS 12
|
|
51
51
|
needs Node.js `>=22.12`. `engines` stays `>=22` because the 11 end does not
|
|
52
52
|
need more; Node 22.0–22.11 satisfies it and still cannot load NestJS 12. CI's
|
|
53
|
-
NestJS 12 leg runs on a current 22.x.
|
|
53
|
+
NestJS 12 leg runs on a current 22.x. Both ends of the NestJS range are tested
|
|
54
|
+
claims: the `11 floor` CI leg pins `11.0.0` exactly (nothing this package uses
|
|
55
|
+
was added by a later 11.x) and the `12` leg floats on `^12`.
|
|
54
56
|
|
|
55
57
|
## Entry points
|
|
56
58
|
|
|
@@ -102,10 +104,11 @@ export class WelcomeEmailHandler implements JobHandler {
|
|
|
102
104
|
|
|
103
105
|
// worker (same process or a dedicated one)
|
|
104
106
|
const controller = new AbortController();
|
|
105
|
-
|
|
107
|
+
runWorkerLoop(app.get(JobsClaimer), { signal: controller.signal })
|
|
108
|
+
.catch((error) => console.error('jobs worker stopped', error));
|
|
106
109
|
```
|
|
107
110
|
|
|
108
|
-
Delivery is **at-least-once**: a worker crash mid-job means the row is reclaimed after `stuckTimeoutMs` and run again. Make handlers idempotent or key their side effects on `ctx.jobId`.
|
|
111
|
+
Delivery is **at-least-once**: a worker crash mid-job means the row is reclaimed after `stuckTimeoutMs` and run again. Make handlers idempotent or key their side effects on `ctx.jobId`. Outcomes are recorded under the claim: a worker whose claim was taken over records nothing, and the job counts as `lost` in its `TickReport`.
|
|
109
112
|
|
|
110
113
|
### The uniqueKey contract
|
|
111
114
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EnqueueJobInput, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
1
|
+
import type { EnqueueJobInput, JobClaim, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
2
2
|
/**
|
|
3
3
|
* MySQL surfaces a unique-constraint violation as error code `ER_DUP_ENTRY`
|
|
4
4
|
* (errno `1062`). mysql2 sets both `error.code === 'ER_DUP_ENTRY'` and
|
|
@@ -29,7 +29,7 @@ export declare class MysqlJobStore implements JobStore {
|
|
|
29
29
|
*/
|
|
30
30
|
private findActiveDuplicate;
|
|
31
31
|
claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
|
|
32
|
-
markCompleted(db: unknown,
|
|
33
|
-
retry(db: unknown,
|
|
34
|
-
markFailed(db: unknown,
|
|
32
|
+
markCompleted(db: unknown, claim: JobClaim): Promise<boolean>;
|
|
33
|
+
retry(db: unknown, claim: JobClaim, delayMs: number, lastError?: string): Promise<boolean>;
|
|
34
|
+
markFailed(db: unknown, claim: JobClaim, reason: string): Promise<boolean>;
|
|
35
35
|
}
|
|
@@ -6,6 +6,12 @@ const node_crypto_1 = require("node:crypto");
|
|
|
6
6
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
7
7
|
const enqueue_input_1 = require("../../enqueue-input");
|
|
8
8
|
const schema_1 = require("./schema");
|
|
9
|
+
/**
|
|
10
|
+
* Matches the job only while it is `processing` under exactly this claim, so a
|
|
11
|
+
* transition from a worker whose claim was taken over after the stuck timeout
|
|
12
|
+
* writes nothing, even when a later claim reused the same `workerInstanceId`.
|
|
13
|
+
*/
|
|
14
|
+
const heldBy = (claim) => (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.id, claim.id), (0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedBy, claim.claimedBy), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedAt, claim.claimedAt));
|
|
9
15
|
/**
|
|
10
16
|
* MySQL surfaces a unique-constraint violation as error code `ER_DUP_ENTRY`
|
|
11
17
|
* (errno `1062`). mysql2 sets both `error.code === 'ER_DUP_ENTRY'` and
|
|
@@ -90,7 +96,10 @@ class MysqlJobStore {
|
|
|
90
96
|
.from(schema_1.jobs)
|
|
91
97
|
.where((0, drizzle_orm_1.or)((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'pending'), (0, drizzle_orm_1.lte)(schema_1.jobs.availableAt, nowIso)), (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.lte)(schema_1.jobs.claimedAt, stuckCutoff))))
|
|
92
98
|
.orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt))
|
|
93
|
-
.limit(cfg.batchSize)
|
|
99
|
+
.limit(cfg.batchSize)
|
|
100
|
+
// FOR UPDATE SKIP LOCKED: two workers claiming at once split the
|
|
101
|
+
// backlog instead of both taking (and running) the same jobs.
|
|
102
|
+
.for('update', { skipLocked: true });
|
|
94
103
|
if (candidates.length === 0)
|
|
95
104
|
return [];
|
|
96
105
|
const ids = candidates.map((c) => c.id);
|
|
@@ -103,10 +112,13 @@ class MysqlJobStore {
|
|
|
103
112
|
.from(schema_1.jobs)
|
|
104
113
|
.where((0, drizzle_orm_1.inArray)(schema_1.jobs.id, ids))
|
|
105
114
|
.orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt));
|
|
106
|
-
|
|
115
|
+
// READ COMMITTED, not InnoDB's default REPEATABLE READ: there the locking
|
|
116
|
+
// read also locks the gaps it scans, so every concurrent enqueue INSERT
|
|
117
|
+
// would wait for the claim to commit.
|
|
118
|
+
}, { isolationLevel: 'read committed' });
|
|
107
119
|
}
|
|
108
|
-
async markCompleted(db,
|
|
109
|
-
await db
|
|
120
|
+
async markCompleted(db, claim) {
|
|
121
|
+
const [result] = await db
|
|
110
122
|
.update(schema_1.jobs)
|
|
111
123
|
.set({
|
|
112
124
|
status: 'completed',
|
|
@@ -115,11 +127,12 @@ class MysqlJobStore {
|
|
|
115
127
|
// Terminal → release the active-dedup key.
|
|
116
128
|
uniqueKey: null,
|
|
117
129
|
})
|
|
118
|
-
.where((
|
|
130
|
+
.where(heldBy(claim));
|
|
131
|
+
return result.affectedRows > 0;
|
|
119
132
|
}
|
|
120
|
-
async retry(db,
|
|
133
|
+
async retry(db, claim, delayMs, lastError) {
|
|
121
134
|
const nextAvailable = new Date(Date.now() + delayMs).toISOString();
|
|
122
|
-
await db
|
|
135
|
+
const [result] = await db
|
|
123
136
|
.update(schema_1.jobs)
|
|
124
137
|
.set({
|
|
125
138
|
status: 'pending',
|
|
@@ -130,10 +143,11 @@ class MysqlJobStore {
|
|
|
130
143
|
lastError: lastError ?? null,
|
|
131
144
|
// Still active → the uniqueKey stays claimed.
|
|
132
145
|
})
|
|
133
|
-
.where((
|
|
146
|
+
.where(heldBy(claim));
|
|
147
|
+
return result.affectedRows > 0;
|
|
134
148
|
}
|
|
135
|
-
async markFailed(db,
|
|
136
|
-
await db
|
|
149
|
+
async markFailed(db, claim, reason) {
|
|
150
|
+
const [result] = await db
|
|
137
151
|
.update(schema_1.jobs)
|
|
138
152
|
.set({
|
|
139
153
|
status: 'failed',
|
|
@@ -143,7 +157,8 @@ class MysqlJobStore {
|
|
|
143
157
|
// Terminal → release the active-dedup key.
|
|
144
158
|
uniqueKey: null,
|
|
145
159
|
})
|
|
146
|
-
.where((
|
|
160
|
+
.where(heldBy(claim));
|
|
161
|
+
return result.affectedRows > 0;
|
|
147
162
|
}
|
|
148
163
|
}
|
|
149
164
|
exports.MysqlJobStore = MysqlJobStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/mysql/job-store.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/mysql/job-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EnqueueJobInput, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
1
|
+
import type { EnqueueJobInput, JobClaim, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
2
2
|
/**
|
|
3
3
|
* Postgres surfaces a unique-constraint violation with SQLSTATE `23505`
|
|
4
4
|
* (`unique_violation`). node-postgres exposes it as `error.code === '23505'`;
|
|
@@ -26,7 +26,7 @@ export declare class PostgresJobStore implements JobStore {
|
|
|
26
26
|
*/
|
|
27
27
|
private findActiveDuplicate;
|
|
28
28
|
claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
|
|
29
|
-
markCompleted(db: unknown,
|
|
30
|
-
retry(db: unknown,
|
|
31
|
-
markFailed(db: unknown,
|
|
29
|
+
markCompleted(db: unknown, claim: JobClaim): Promise<boolean>;
|
|
30
|
+
retry(db: unknown, claim: JobClaim, delayMs: number, lastError?: string): Promise<boolean>;
|
|
31
|
+
markFailed(db: unknown, claim: JobClaim, reason: string): Promise<boolean>;
|
|
32
32
|
}
|
|
@@ -6,6 +6,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
6
6
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
7
7
|
const enqueue_input_1 = require("../../enqueue-input");
|
|
8
8
|
const schema_1 = require("./schema");
|
|
9
|
+
const transaction_1 = require("./transaction");
|
|
9
10
|
/**
|
|
10
11
|
* Postgres surfaces a unique-constraint violation with SQLSTATE `23505`
|
|
11
12
|
* (`unique_violation`). node-postgres exposes it as `error.code === '23505'`;
|
|
@@ -21,6 +22,22 @@ function hasCode(error, code) {
|
|
|
21
22
|
'code' in error &&
|
|
22
23
|
error.code === code);
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Matches the job only while it is `processing` under exactly this claim, so a
|
|
27
|
+
* transition from a worker whose claim was taken over after the stuck timeout
|
|
28
|
+
* writes nothing, even when a later claim reused the same `workerInstanceId`.
|
|
29
|
+
*/
|
|
30
|
+
const heldBy = (claim) => (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.id, claim.id), (0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedBy, claim.claimedBy), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedAt, claim.claimedAt));
|
|
31
|
+
/**
|
|
32
|
+
* Runs one fenced transition and reports whether it wrote the job. Each runs in
|
|
33
|
+
* its own READ COMMITTED transaction, like the claim: under a SERIALIZABLE
|
|
34
|
+
* server default the fenced UPDATE's scan (on the status index, in steady state)
|
|
35
|
+
* makes two workers' transitions abort each other (40001) while both claims
|
|
36
|
+
* still hold their jobs. Under READ COMMITTED an UPDATE that waited on a
|
|
37
|
+
* reclaim re-checks the fence against the committed row instead, so `false`
|
|
38
|
+
* means the claim really was taken over, and any error is a real one.
|
|
39
|
+
*/
|
|
40
|
+
const fenced = (db, update) => (0, transaction_1.readCommitted)(db, async (tx) => (await update(tx)).length > 0);
|
|
24
41
|
/**
|
|
25
42
|
* Postgres (node-postgres) job store. Every method is **asynchronous** —
|
|
26
43
|
* `enqueue` awaits the insert (call it with `await` inside an async
|
|
@@ -79,13 +96,16 @@ class PostgresJobStore {
|
|
|
79
96
|
const now = new Date();
|
|
80
97
|
const nowIso = now.toISOString();
|
|
81
98
|
const stuckCutoff = new Date(now.getTime() - cfg.stuckTimeoutMs).toISOString();
|
|
82
|
-
|
|
99
|
+
// FOR UPDATE SKIP LOCKED: two workers claiming at once split the backlog
|
|
100
|
+
// instead of both taking (and running) the same jobs.
|
|
101
|
+
return (0, transaction_1.readCommitted)(db, async (tx) => {
|
|
83
102
|
const candidates = await tx
|
|
84
103
|
.select({ id: schema_1.jobs.id })
|
|
85
104
|
.from(schema_1.jobs)
|
|
86
105
|
.where((0, drizzle_orm_1.or)((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'pending'), (0, drizzle_orm_1.lte)(schema_1.jobs.availableAt, nowIso)), (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.lte)(schema_1.jobs.claimedAt, stuckCutoff))))
|
|
87
106
|
.orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt))
|
|
88
|
-
.limit(cfg.batchSize)
|
|
107
|
+
.limit(cfg.batchSize)
|
|
108
|
+
.for('update', { skipLocked: true });
|
|
89
109
|
if (candidates.length === 0)
|
|
90
110
|
return [];
|
|
91
111
|
const ids = candidates.map((c) => c.id);
|
|
@@ -100,8 +120,8 @@ class PostgresJobStore {
|
|
|
100
120
|
.orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt));
|
|
101
121
|
});
|
|
102
122
|
}
|
|
103
|
-
async markCompleted(db,
|
|
104
|
-
|
|
123
|
+
async markCompleted(db, claim) {
|
|
124
|
+
return fenced(db, (tx) => tx
|
|
105
125
|
.update(schema_1.jobs)
|
|
106
126
|
.set({
|
|
107
127
|
status: 'completed',
|
|
@@ -110,11 +130,12 @@ class PostgresJobStore {
|
|
|
110
130
|
// Terminal → release the active-dedup key.
|
|
111
131
|
uniqueKey: null,
|
|
112
132
|
})
|
|
113
|
-
.where((
|
|
133
|
+
.where(heldBy(claim))
|
|
134
|
+
.returning({ id: schema_1.jobs.id }));
|
|
114
135
|
}
|
|
115
|
-
async retry(db,
|
|
136
|
+
async retry(db, claim, delayMs, lastError) {
|
|
116
137
|
const nextAvailable = new Date(Date.now() + delayMs).toISOString();
|
|
117
|
-
|
|
138
|
+
return fenced(db, (tx) => tx
|
|
118
139
|
.update(schema_1.jobs)
|
|
119
140
|
.set({
|
|
120
141
|
status: 'pending',
|
|
@@ -125,10 +146,11 @@ class PostgresJobStore {
|
|
|
125
146
|
lastError: lastError ?? null,
|
|
126
147
|
// Still active → the uniqueKey stays claimed.
|
|
127
148
|
})
|
|
128
|
-
.where((
|
|
149
|
+
.where(heldBy(claim))
|
|
150
|
+
.returning({ id: schema_1.jobs.id }));
|
|
129
151
|
}
|
|
130
|
-
async markFailed(db,
|
|
131
|
-
|
|
152
|
+
async markFailed(db, claim, reason) {
|
|
153
|
+
return fenced(db, (tx) => tx
|
|
132
154
|
.update(schema_1.jobs)
|
|
133
155
|
.set({
|
|
134
156
|
status: 'failed',
|
|
@@ -138,7 +160,8 @@ class PostgresJobStore {
|
|
|
138
160
|
// Terminal → release the active-dedup key.
|
|
139
161
|
uniqueKey: null,
|
|
140
162
|
})
|
|
141
|
-
.where((
|
|
163
|
+
.where(heldBy(claim))
|
|
164
|
+
.returning({ id: schema_1.jobs.id }));
|
|
142
165
|
}
|
|
143
166
|
}
|
|
144
167
|
exports.PostgresJobStore = PostgresJobStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/postgres/job-store.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/postgres/job-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ exports.PostgresScheduleStore = void 0;
|
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
6
6
|
const schema_1 = require("./schema");
|
|
7
|
+
const transaction_1 = require("./transaction");
|
|
7
8
|
/**
|
|
8
9
|
* Postgres (node-postgres) schedule store. Every method is **asynchronous**,
|
|
9
10
|
* mirroring {@link PostgresJobStore}.
|
|
@@ -95,7 +96,7 @@ class PostgresScheduleStore {
|
|
|
95
96
|
.limit(limit);
|
|
96
97
|
}
|
|
97
98
|
async claimAndEnqueue(db, claim) {
|
|
98
|
-
return
|
|
99
|
+
return (0, transaction_1.readCommitted)(db, async (tx) => {
|
|
99
100
|
const cas = await tx
|
|
100
101
|
.update(schema_1.jobSchedules)
|
|
101
102
|
.set({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-store.js","sourceRoot":"","sources":["../../../dialects/postgres/schedule-store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"schedule-store.js","sourceRoot":"","sources":["../../../dialects/postgres/schedule-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
2
|
+
type Db = NodePgDatabase<Record<string, never>>;
|
|
3
|
+
/**
|
|
4
|
+
* Runs `work` in its own READ COMMITTED transaction, whatever the server
|
|
5
|
+
* default: a claim's locking scan then skips rows another claim holds instead
|
|
6
|
+
* of failing with 40001 (Postgres) or gap-locking every concurrent enqueue.
|
|
7
|
+
*
|
|
8
|
+
* On a node-postgres `Pool` it checks the client out itself instead of going
|
|
9
|
+
* through drizzle's `transaction()`, which leaves the checked-out client without
|
|
10
|
+
* an `error` listener and sends BEGIN outside its cleanup. A connection the
|
|
11
|
+
* server dropped mid-transaction (a failover, `pg_terminate_backend`) then
|
|
12
|
+
* crashed the process, and one dropped at BEGIN was never returned to the pool.
|
|
13
|
+
* Here the client listens for errors while it is out, a failed ROLLBACK never
|
|
14
|
+
* hides the original error, and a broken connection is released with its error
|
|
15
|
+
* so the pool discards it. Anything else (PGlite, a single `Client`) goes through
|
|
16
|
+
* drizzle's `transaction()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readCommitted<T>(db: unknown, work: (tx: Db) => Promise<T>): Promise<T>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readCommitted = readCommitted;
|
|
37
|
+
const common_1 = require("@nestjs/common");
|
|
38
|
+
const isPgPool = (client) => typeof client === 'object' &&
|
|
39
|
+
client !== null &&
|
|
40
|
+
typeof client.connect === 'function' &&
|
|
41
|
+
typeof client.totalCount === 'number';
|
|
42
|
+
const logger = new common_1.Logger('JobsPostgres');
|
|
43
|
+
// Pools already warned that they have no `error` listener.
|
|
44
|
+
const unguardedPools = new WeakSet();
|
|
45
|
+
/**
|
|
46
|
+
* Warns once per pool that has no `error` listener. node-postgres reports a
|
|
47
|
+
* connection an idle client loses as the pool's `error` event, and with no
|
|
48
|
+
* listener that event crashes the process: every client the stores release
|
|
49
|
+
* goes back into that state, so the listener is load-bearing.
|
|
50
|
+
*/
|
|
51
|
+
function warnIfUnguarded(pool) {
|
|
52
|
+
if (unguardedPools.has(pool) || pool.listenerCount?.('error') !== 0)
|
|
53
|
+
return;
|
|
54
|
+
unguardedPools.add(pool);
|
|
55
|
+
logger.warn("the node-postgres Pool the jobs stores run on has no 'error' listener; node-postgres crashes the process when an idle client loses its connection, so add pool.on('error', ...)");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Runs `work` in its own READ COMMITTED transaction, whatever the server
|
|
59
|
+
* default: a claim's locking scan then skips rows another claim holds instead
|
|
60
|
+
* of failing with 40001 (Postgres) or gap-locking every concurrent enqueue.
|
|
61
|
+
*
|
|
62
|
+
* On a node-postgres `Pool` it checks the client out itself instead of going
|
|
63
|
+
* through drizzle's `transaction()`, which leaves the checked-out client without
|
|
64
|
+
* an `error` listener and sends BEGIN outside its cleanup. A connection the
|
|
65
|
+
* server dropped mid-transaction (a failover, `pg_terminate_backend`) then
|
|
66
|
+
* crashed the process, and one dropped at BEGIN was never returned to the pool.
|
|
67
|
+
* Here the client listens for errors while it is out, a failed ROLLBACK never
|
|
68
|
+
* hides the original error, and a broken connection is released with its error
|
|
69
|
+
* so the pool discards it. Anything else (PGlite, a single `Client`) goes through
|
|
70
|
+
* drizzle's `transaction()`.
|
|
71
|
+
*/
|
|
72
|
+
async function readCommitted(db, work) {
|
|
73
|
+
const pool = db.$client;
|
|
74
|
+
if (!isPgPool(pool)) {
|
|
75
|
+
return db.transaction((tx) => work(tx), {
|
|
76
|
+
isolationLevel: 'read committed',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
warnIfUnguarded(pool);
|
|
80
|
+
// The pool is shaped like node-postgres's, so its drizzle driver (and `pg`)
|
|
81
|
+
// loads here, before a client is checked out. The caller's query logger and
|
|
82
|
+
// cache carry over, so the stores' statements show up where the
|
|
83
|
+
// application's do.
|
|
84
|
+
const { drizzle } = await Promise.resolve().then(() => __importStar(require('drizzle-orm/node-postgres')));
|
|
85
|
+
const { logger: queryLogger, cache } = db.session?.options ?? {};
|
|
86
|
+
const client = await pool.connect();
|
|
87
|
+
let broken;
|
|
88
|
+
const onError = (error) => {
|
|
89
|
+
broken = error;
|
|
90
|
+
};
|
|
91
|
+
client.on('error', onError);
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
await client.query('begin isolation level read committed');
|
|
95
|
+
result = await work(drizzle(client, { logger: queryLogger, cache }));
|
|
96
|
+
await client.query('commit');
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
await client.query('rollback').catch((rollbackError) => {
|
|
100
|
+
broken ??= rollbackError;
|
|
101
|
+
});
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
client.removeListener('error', onError);
|
|
106
|
+
client.release(broken);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../dialects/postgres/transaction.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,sCAmCC;AAjGD,2CAAwC;AAkBxC,MAAM,QAAQ,GAAG,CAAC,MAAe,EAAoB,EAAE,CACrD,OAAO,MAAM,KAAK,QAAQ;IAC1B,MAAM,KAAK,IAAI;IACf,OAAQ,MAA0B,CAAC,OAAO,KAAK,UAAU;IACzD,OAAQ,MAA0B,CAAC,UAAU,KAAK,QAAQ,CAAC;AAE7D,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,cAAc,CAAC,CAAC;AAC1C,2DAA2D;AAC3D,MAAM,cAAc,GAAG,IAAI,OAAO,EAAU,CAAC;AAE7C;;;;;GAKG;AACH,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO;IAC5E,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,IAAI,CACT,iLAAiL,CAClL,CAAC;AACJ,CAAC;AAOD;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,aAAa,CAAI,EAAW,EAAE,IAA4B;IAC9E,MAAM,IAAI,GAAI,EAA4B,CAAC,OAAO,CAAC;IACnD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpB,OAAQ,EAAS,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAmB,CAAC,EAAE;YAC/D,cAAc,EAAE,gBAAgB;SACjC,CAAC,CAAC;IACL,CAAC;IACD,eAAe,CAAC,IAAI,CAAC,CAAC;IACtB,4EAA4E;IAC5E,4EAA4E;IAC5E,gEAAgE;IAChE,oBAAoB;IACpB,MAAM,EAAE,OAAO,EAAE,GAAG,wDAAa,2BAA2B,GAAC,CAAC;IAC9D,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAI,EAA0B,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;IAC1F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,MAAyB,CAAC;IAC9B,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE;QACrC,MAAM,GAAG,KAAK,CAAC;IACjB,CAAC,CAAC;IACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5B,IAAI,MAAS,CAAC;IACd,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC3D,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAe,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAW,CAAkB,CAAC,CAAC;QACxG,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,aAAoB,EAAE,EAAE;YAC5D,MAAM,KAAK,aAAa,CAAC;QAC3B,CAAC,CAAC,CAAC;QACH,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EnqueueJobInput, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
1
|
+
import type { EnqueueJobInput, JobClaim, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
|
|
2
2
|
/**
|
|
3
3
|
* better-sqlite3 surfaces a unique-constraint violation as a `SqliteError` with
|
|
4
4
|
* `code === 'SQLITE_CONSTRAINT_UNIQUE'`. Match on the code (stable across driver
|
|
@@ -26,7 +26,7 @@ export declare class SqliteJobStore implements JobStore {
|
|
|
26
26
|
*/
|
|
27
27
|
private findActiveDuplicate;
|
|
28
28
|
claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
|
|
29
|
-
markCompleted(db: unknown,
|
|
30
|
-
retry(db: unknown,
|
|
31
|
-
markFailed(db: unknown,
|
|
29
|
+
markCompleted(db: unknown, claim: JobClaim): Promise<boolean>;
|
|
30
|
+
retry(db: unknown, claim: JobClaim, delayMs: number, lastError?: string): Promise<boolean>;
|
|
31
|
+
markFailed(db: unknown, claim: JobClaim, reason: string): Promise<boolean>;
|
|
32
32
|
}
|
|
@@ -22,6 +22,12 @@ function hasCode(error, code) {
|
|
|
22
22
|
'code' in error &&
|
|
23
23
|
error.code === code);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Matches the job only while it is `processing` under exactly this claim, so a
|
|
27
|
+
* transition from a worker whose claim was taken over after the stuck timeout
|
|
28
|
+
* writes nothing, even when a later claim reused the same `workerInstanceId`.
|
|
29
|
+
*/
|
|
30
|
+
const heldBy = (claim) => (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.id, claim.id), (0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedBy, claim.claimedBy), (0, drizzle_orm_1.eq)(schema_1.jobs.claimedAt, claim.claimedAt));
|
|
25
31
|
/**
|
|
26
32
|
* SQLite (better-sqlite3) job store. Every method runs **synchronously** —
|
|
27
33
|
* `enqueue` returns the row directly so it composes inside a synchronous
|
|
@@ -104,8 +110,8 @@ class SqliteJobStore {
|
|
|
104
110
|
});
|
|
105
111
|
return Promise.resolve(rows);
|
|
106
112
|
}
|
|
107
|
-
markCompleted(db,
|
|
108
|
-
db
|
|
113
|
+
markCompleted(db, claim) {
|
|
114
|
+
const { changes } = db
|
|
109
115
|
.update(schema_1.jobs)
|
|
110
116
|
.set({
|
|
111
117
|
status: 'completed',
|
|
@@ -114,13 +120,13 @@ class SqliteJobStore {
|
|
|
114
120
|
// Terminal → release the active-dedup key.
|
|
115
121
|
uniqueKey: null,
|
|
116
122
|
})
|
|
117
|
-
.where((
|
|
123
|
+
.where(heldBy(claim))
|
|
118
124
|
.run();
|
|
119
|
-
return Promise.resolve();
|
|
125
|
+
return Promise.resolve(changes > 0);
|
|
120
126
|
}
|
|
121
|
-
retry(db,
|
|
127
|
+
retry(db, claim, delayMs, lastError) {
|
|
122
128
|
const nextAvailable = new Date(Date.now() + delayMs).toISOString();
|
|
123
|
-
db
|
|
129
|
+
const { changes } = db
|
|
124
130
|
.update(schema_1.jobs)
|
|
125
131
|
.set({
|
|
126
132
|
status: 'pending',
|
|
@@ -131,12 +137,12 @@ class SqliteJobStore {
|
|
|
131
137
|
lastError: lastError ?? null,
|
|
132
138
|
// Still active → the uniqueKey stays claimed.
|
|
133
139
|
})
|
|
134
|
-
.where((
|
|
140
|
+
.where(heldBy(claim))
|
|
135
141
|
.run();
|
|
136
|
-
return Promise.resolve();
|
|
142
|
+
return Promise.resolve(changes > 0);
|
|
137
143
|
}
|
|
138
|
-
markFailed(db,
|
|
139
|
-
db
|
|
144
|
+
markFailed(db, claim, reason) {
|
|
145
|
+
const { changes } = db
|
|
140
146
|
.update(schema_1.jobs)
|
|
141
147
|
.set({
|
|
142
148
|
status: 'failed',
|
|
@@ -146,9 +152,9 @@ class SqliteJobStore {
|
|
|
146
152
|
// Terminal → release the active-dedup key.
|
|
147
153
|
uniqueKey: null,
|
|
148
154
|
})
|
|
149
|
-
.where((
|
|
155
|
+
.where(heldBy(claim))
|
|
150
156
|
.run();
|
|
151
|
-
return Promise.resolve();
|
|
157
|
+
return Promise.resolve(changes > 0);
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
160
|
exports.SqliteJobStore = SqliteJobStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/sqlite/job-store.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/sqlite/job-store.ts"],"names":[],"mappings":"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"}
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -92,17 +92,42 @@ export interface JobContext {
|
|
|
92
92
|
*
|
|
93
93
|
* `claimBatch` opens its own transaction, claims due `pending` jobs plus
|
|
94
94
|
* `processing` jobs stuck past `stuckTimeoutMs`, ordered by `priority DESC,
|
|
95
|
-
* available_at ASC`, and marks them `processing`.
|
|
95
|
+
* available_at ASC`, and marks them `processing`. It must be atomic across
|
|
96
|
+
* workers: two concurrent calls never return the same job (the shipped
|
|
97
|
+
* Postgres and MySQL stores use `FOR UPDATE SKIP LOCKED`). It stamps every job
|
|
98
|
+
* it returns with `claimedBy` and `claimedAt`, and returns each job as that
|
|
99
|
+
* UPDATE left it. The claimer refuses a job without a string stamp, but it
|
|
100
|
+
* cannot tell a stale stamp from the one the claim wrote: a reclaimed job read
|
|
101
|
+
* before the UPDATE carries the previous claim's stamp, and runs under a claim
|
|
102
|
+
* no transition matches, again after every stuck timeout.
|
|
103
|
+
*
|
|
104
|
+
* The three transitions take that stamp back as a {@link JobClaim} and apply
|
|
105
|
+
* only while the job still holds it, resolving `false` (and writing nothing)
|
|
106
|
+
* once the claim has been taken over. A database error rejects; it never reads
|
|
107
|
+
* as `false`.
|
|
96
108
|
*/
|
|
97
109
|
export interface JobStore {
|
|
98
110
|
enqueue(db: unknown, input: EnqueueJobInput<object>): JobRow | Promise<JobRow>;
|
|
99
111
|
claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
|
|
100
112
|
/** Terminal: sets `completed` and clears `uniqueKey` (releases the key). */
|
|
101
|
-
markCompleted(db: unknown,
|
|
113
|
+
markCompleted(db: unknown, claim: JobClaim): Promise<boolean>;
|
|
102
114
|
/** Re-arms the job: `pending`, attempts+1, due in `delayMs`. Keeps `uniqueKey`. */
|
|
103
|
-
retry(db: unknown,
|
|
115
|
+
retry(db: unknown, claim: JobClaim, delayMs: number, lastError?: string): Promise<boolean>;
|
|
104
116
|
/** Terminal: sets `failed`, attempts+1, and clears `uniqueKey`. */
|
|
105
|
-
markFailed(db: unknown,
|
|
117
|
+
markFailed(db: unknown, claim: JobClaim, reason: string): Promise<boolean>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The claim a job transition must still hold: the job's `id` plus the stamp
|
|
121
|
+
* `claimBatch` wrote. A transition matches the job only while it is
|
|
122
|
+
* `processing` under exactly this `claimedBy` and `claimedAt`, so a worker
|
|
123
|
+
* whose claim was taken over after the stuck timeout (by another worker, or by
|
|
124
|
+
* a later claim under the same `workerInstanceId`) can never overwrite the new
|
|
125
|
+
* owner's outcome.
|
|
126
|
+
*/
|
|
127
|
+
export interface JobClaim {
|
|
128
|
+
readonly id: string;
|
|
129
|
+
readonly claimedBy: string;
|
|
130
|
+
readonly claimedAt: string;
|
|
106
131
|
}
|
|
107
132
|
/**
|
|
108
133
|
* The dialect-agnostic shape of a `job_schedules` row. Like {@link JobRow},
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { JobStore, ResolvedRunnerConfig, RunnerConfig, ScheduleStore } from './interfaces';
|
|
2
2
|
import { JobsHandlerExplorer } from './jobs-handler.explorer';
|
|
3
3
|
export declare const DEFAULT_RUNNER_CONFIG: ResolvedRunnerConfig;
|
|
4
|
+
/**
|
|
5
|
+
* Applies `overrides` over {@link DEFAULT_RUNNER_CONFIG}, as `tick()` does. A
|
|
6
|
+
* key set to `undefined` keeps its default: `{ workerInstanceId:
|
|
7
|
+
* process.env.WORKER_ID }` with the variable unset must not claim jobs under no
|
|
8
|
+
* owner, which no transition could then match. A value that would break
|
|
9
|
+
* claiming throws, so a worker can call this at startup to check its config.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveRunnerConfig(overrides?: RunnerConfig | null): ResolvedRunnerConfig;
|
|
4
12
|
export interface TickReport {
|
|
5
13
|
/** Schedule occurrences THIS tick enqueued (won claims; dedup-suppressed occurrences still count as claimed). */
|
|
6
14
|
scheduled: number;
|
|
@@ -8,6 +16,16 @@ export interface TickReport {
|
|
|
8
16
|
completed: number;
|
|
9
17
|
retried: number;
|
|
10
18
|
failed: number;
|
|
19
|
+
/**
|
|
20
|
+
* Claimed jobs this worker let go without recording an outcome. Either the
|
|
21
|
+
* batch had been held longer than `stuckTimeoutMs` by the time the job's turn
|
|
22
|
+
* came, so it never ran here, or another claim took the job over before the
|
|
23
|
+
* outcome could be recorded; whoever claims such a job next runs it, and a
|
|
24
|
+
* steady non-zero count means batches take longer than `stuckTimeoutMs`. A
|
|
25
|
+
* job the store returned without its claim's stamp counts here too, logged
|
|
26
|
+
* as an error.
|
|
27
|
+
*/
|
|
28
|
+
lost: number;
|
|
11
29
|
}
|
|
12
30
|
/**
|
|
13
31
|
* Executes committed jobs. `tick()` claims a batch of due jobs (the store opens
|
|
@@ -22,6 +40,10 @@ export interface TickReport {
|
|
|
22
40
|
* until `maxAttempts`, then `markFailed`
|
|
23
41
|
* - no handler registered → `PermanentError` → `markFailed`
|
|
24
42
|
*
|
|
43
|
+
* Each outcome is recorded under the job's claim: once another worker has
|
|
44
|
+
* taken the job over (its claim went stale after `stuckTimeoutMs`), the
|
|
45
|
+
* transition writes nothing, and the job counts as `lost` instead.
|
|
46
|
+
*
|
|
25
47
|
* Runs in a background worker (see `runWorkerLoop`) — never inside a business
|
|
26
48
|
* transaction — so it freely awaits the store and the handlers.
|
|
27
49
|
*/
|
|
@@ -45,6 +67,19 @@ export declare class JobsClaimer {
|
|
|
45
67
|
private drainSchedules;
|
|
46
68
|
private fireSchedule;
|
|
47
69
|
private processOne;
|
|
70
|
+
/**
|
|
71
|
+
* The claim to run under, or `undefined` when `claimedBy` or `claimedAt` is
|
|
72
|
+
* not a string: run without a usable stamp, the job would execute with no
|
|
73
|
+
* transition able to record it, and again after every stuck timeout. Whether
|
|
74
|
+
* a string stamp is the one the claim wrote cannot be told apart here; that
|
|
75
|
+
* is the store's contract.
|
|
76
|
+
*/
|
|
77
|
+
private claimOf;
|
|
78
|
+
/**
|
|
79
|
+
* `outcome` when the transition applied; otherwise another claim took the
|
|
80
|
+
* job over first. `reason` is the handler's error, for the outcomes it caused.
|
|
81
|
+
*/
|
|
82
|
+
private settle;
|
|
48
83
|
private onHandlerError;
|
|
49
84
|
private fail;
|
|
50
85
|
private backoff;
|
|
@@ -14,6 +14,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
var JobsClaimer_1;
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.JobsClaimer = exports.DEFAULT_RUNNER_CONFIG = void 0;
|
|
17
|
+
exports.resolveRunnerConfig = resolveRunnerConfig;
|
|
17
18
|
const node_os_1 = require("node:os");
|
|
18
19
|
const common_1 = require("@nestjs/common");
|
|
19
20
|
const errors_1 = require("./errors");
|
|
@@ -27,6 +28,44 @@ exports.DEFAULT_RUNNER_CONFIG = {
|
|
|
27
28
|
baseBackoffMs: 1_000,
|
|
28
29
|
maxBackoffMs: 60_000,
|
|
29
30
|
};
|
|
31
|
+
// The furthest a Date can sit from the epoch: a stuck cutoff beyond it is an
|
|
32
|
+
// invalid date, and every claim would throw.
|
|
33
|
+
const MAX_DATE_OFFSET_MS = 8.64e15;
|
|
34
|
+
// A stamp value for a log line: a string quoted, anything else by its type, so
|
|
35
|
+
// a Date does not pass for a valid stamp and a BigInt cannot throw.
|
|
36
|
+
const stampShown = (value) => typeof value === 'string' ? JSON.stringify(value) : Object.prototype.toString.call(value).slice(8, -1);
|
|
37
|
+
// A rejected value as the caller wrote it: a string from an env-backed config
|
|
38
|
+
// keeps its quotes, so `"60000" (string)` does not pass for the number 60000.
|
|
39
|
+
const shown = (value) => typeof value === 'number' ? String(value) : `${JSON.stringify(value)} (${typeof value})`;
|
|
40
|
+
/**
|
|
41
|
+
* Applies `overrides` over {@link DEFAULT_RUNNER_CONFIG}, as `tick()` does. A
|
|
42
|
+
* key set to `undefined` keeps its default: `{ workerInstanceId:
|
|
43
|
+
* process.env.WORKER_ID }` with the variable unset must not claim jobs under no
|
|
44
|
+
* owner, which no transition could then match. A value that would break
|
|
45
|
+
* claiming throws, so a worker can call this at startup to check its config.
|
|
46
|
+
*/
|
|
47
|
+
function resolveRunnerConfig(overrides = {}) {
|
|
48
|
+
const defined = Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== undefined));
|
|
49
|
+
const cfg = { ...exports.DEFAULT_RUNNER_CONFIG, ...defined };
|
|
50
|
+
if (typeof cfg.workerInstanceId !== 'string' || cfg.workerInstanceId.trim() === '') {
|
|
51
|
+
throw new TypeError(`runner workerInstanceId must be a non-empty string, got ${shown(cfg.workerInstanceId)}`);
|
|
52
|
+
}
|
|
53
|
+
if (!Number.isInteger(cfg.batchSize) || cfg.batchSize < 1) {
|
|
54
|
+
throw new RangeError(`runner batchSize must be a positive integer, got ${shown(cfg.batchSize)}`);
|
|
55
|
+
}
|
|
56
|
+
// At zero every processing job is stuck the moment it is claimed, so two
|
|
57
|
+
// workers would run the same job side by side.
|
|
58
|
+
const stuck = cfg.stuckTimeoutMs;
|
|
59
|
+
if (!(typeof stuck === 'number' && stuck > 0 && stuck <= MAX_DATE_OFFSET_MS)) {
|
|
60
|
+
throw new RangeError(`runner stuckTimeoutMs must be a positive number of milliseconds within Date's range, got ${shown(stuck)}`);
|
|
61
|
+
}
|
|
62
|
+
for (const key of ['baseBackoffMs', 'maxBackoffMs']) {
|
|
63
|
+
if (!(Number.isFinite(cfg[key]) && cfg[key] >= 0)) {
|
|
64
|
+
throw new RangeError(`runner ${key} must be a non-negative number, got ${shown(cfg[key])}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return cfg;
|
|
68
|
+
}
|
|
30
69
|
/**
|
|
31
70
|
* Executes committed jobs. `tick()` claims a batch of due jobs (the store opens
|
|
32
71
|
* its own transaction; priority first, oldest due first, reclaiming jobs stuck
|
|
@@ -40,6 +79,10 @@ exports.DEFAULT_RUNNER_CONFIG = {
|
|
|
40
79
|
* until `maxAttempts`, then `markFailed`
|
|
41
80
|
* - no handler registered → `PermanentError` → `markFailed`
|
|
42
81
|
*
|
|
82
|
+
* Each outcome is recorded under the job's claim: once another worker has
|
|
83
|
+
* taken the job over (its claim went stale after `stuckTimeoutMs`), the
|
|
84
|
+
* transition writes nothing, and the job counts as `lost` instead.
|
|
85
|
+
*
|
|
43
86
|
* Runs in a background worker (see `runWorkerLoop`) — never inside a business
|
|
44
87
|
* transaction — so it freely awaits the store and the handlers.
|
|
45
88
|
*/
|
|
@@ -52,21 +95,36 @@ let JobsClaimer = JobsClaimer_1 = class JobsClaimer {
|
|
|
52
95
|
this.logger = new common_1.Logger(JobsClaimer_1.name);
|
|
53
96
|
}
|
|
54
97
|
async tick(overrides = {}) {
|
|
55
|
-
const cfg =
|
|
98
|
+
const cfg = resolveRunnerConfig(overrides);
|
|
56
99
|
// Due schedules fire first, so their occurrences (due immediately) are
|
|
57
100
|
// claimable by this very tick's batch claim.
|
|
58
101
|
const scheduled = this.scheduleStore ? await this.drainSchedules(cfg) : 0;
|
|
59
102
|
const claimed = await this.store.claimBatch(this.db, cfg);
|
|
103
|
+
// Measured from the batch's arrival, so its first job always runs and a
|
|
104
|
+
// claim that itself took longer than stuckTimeoutMs still makes progress.
|
|
105
|
+
const heldSince = Date.now();
|
|
60
106
|
const report = {
|
|
61
107
|
scheduled,
|
|
62
108
|
claimed: claimed.length,
|
|
63
109
|
completed: 0,
|
|
64
110
|
retried: 0,
|
|
65
111
|
failed: 0,
|
|
112
|
+
lost: 0,
|
|
66
113
|
};
|
|
114
|
+
// Once the batch has been held past stuckTimeoutMs, another worker may
|
|
115
|
+
// already have reclaimed its remaining jobs; running them here as well
|
|
116
|
+
// would only run them twice.
|
|
117
|
+
const expired = [];
|
|
67
118
|
for (const job of claimed) {
|
|
68
|
-
|
|
69
|
-
|
|
119
|
+
if (Date.now() - heldSince >= cfg.stuckTimeoutMs) {
|
|
120
|
+
expired.push(job.id);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
report[await this.processOne(job, cfg)] += 1;
|
|
124
|
+
}
|
|
125
|
+
if (expired.length > 0) {
|
|
126
|
+
report.lost += expired.length;
|
|
127
|
+
this.logger.warn(`skipped ${expired.length} claimed job(s) [${expired.join(', ')}]: the batch was held longer than stuckTimeoutMs (${cfg.stuckTimeoutMs} ms), so another worker may own them now; raise stuckTimeoutMs or lower batchSize`);
|
|
70
128
|
}
|
|
71
129
|
return report;
|
|
72
130
|
}
|
|
@@ -122,6 +180,9 @@ let JobsClaimer = JobsClaimer_1 = class JobsClaimer {
|
|
|
122
180
|
}
|
|
123
181
|
}
|
|
124
182
|
async processOne(job, cfg) {
|
|
183
|
+
const claim = this.claimOf(job);
|
|
184
|
+
if (claim === undefined)
|
|
185
|
+
return 'lost';
|
|
125
186
|
try {
|
|
126
187
|
const handler = this.explorer.get(job.name);
|
|
127
188
|
if (!handler) {
|
|
@@ -131,36 +192,66 @@ let JobsClaimer = JobsClaimer_1 = class JobsClaimer {
|
|
|
131
192
|
jobId: job.id,
|
|
132
193
|
attempt: job.attempts + 1,
|
|
133
194
|
});
|
|
134
|
-
await this.store.markCompleted(this.db, job.id);
|
|
135
|
-
return 'completed';
|
|
136
195
|
}
|
|
137
196
|
catch (error) {
|
|
138
|
-
return this.onHandlerError(job, cfg, error);
|
|
197
|
+
return this.onHandlerError(job, claim, cfg, error);
|
|
139
198
|
}
|
|
199
|
+
// Outside the try: failing to record a completion is not a failed run.
|
|
200
|
+
// Treating it as one spent an attempt and could mark a job that ran failed.
|
|
201
|
+
// The tick throws instead, and the job, still claimed, runs again once its
|
|
202
|
+
// claim goes stale.
|
|
203
|
+
return this.settle(job, await this.store.markCompleted(this.db, claim), 'completed');
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The claim to run under, or `undefined` when `claimedBy` or `claimedAt` is
|
|
207
|
+
* not a string: run without a usable stamp, the job would execute with no
|
|
208
|
+
* transition able to record it, and again after every stuck timeout. Whether
|
|
209
|
+
* a string stamp is the one the claim wrote cannot be told apart here; that
|
|
210
|
+
* is the store's contract.
|
|
211
|
+
*/
|
|
212
|
+
claimOf(job) {
|
|
213
|
+
const { id, claimedBy, claimedAt } = job;
|
|
214
|
+
if (typeof claimedBy === 'string' && typeof claimedAt === 'string') {
|
|
215
|
+
return { id, claimedBy, claimedAt };
|
|
216
|
+
}
|
|
217
|
+
this.logger.error(`job ${id} came back from claimBatch without a string claim stamp (claimedBy ${stampShown(claimedBy)}, claimedAt ${stampShown(claimedAt)}); a store must return every job it claims as its claiming UPDATE left it, with claimedBy and claimedAt as strings`);
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* `outcome` when the transition applied; otherwise another claim took the
|
|
222
|
+
* job over first. `reason` is the handler's error, for the outcomes it caused.
|
|
223
|
+
*/
|
|
224
|
+
settle(job, applied, outcome, reason) {
|
|
225
|
+
if (applied)
|
|
226
|
+
return outcome;
|
|
227
|
+
this.logger.warn(reason === undefined
|
|
228
|
+
? `job ${job.id} ("${job.name}") ran, but another claim took it over before it was marked completed; the new owner records its outcome and may run it again`
|
|
229
|
+
: `job ${job.id} ("${job.name}") failed (${reason}), but another claim took it over before it was marked ${outcome}; leaving it to the new owner`);
|
|
230
|
+
return 'lost';
|
|
140
231
|
}
|
|
141
|
-
async onHandlerError(job, cfg, error) {
|
|
232
|
+
async onHandlerError(job, claim, cfg, error) {
|
|
142
233
|
const message = error instanceof Error ? error.message : String(error);
|
|
143
234
|
// Permanent: retrying can never succeed — fail now instead of burning attempts.
|
|
144
235
|
if (error instanceof errors_1.PermanentError) {
|
|
145
|
-
return this.fail(job, message);
|
|
236
|
+
return this.fail(job, claim, message);
|
|
146
237
|
}
|
|
147
238
|
// Retryable: schedule another attempt, honouring a handler-supplied delay.
|
|
148
239
|
if (error instanceof errors_1.RetryableError) {
|
|
149
240
|
const delay = error.delayMs ?? this.backoff(job.attempts, cfg);
|
|
150
|
-
await this.store.retry(this.db,
|
|
151
|
-
return 'retried';
|
|
241
|
+
return this.settle(job, await this.store.retry(this.db, claim, delay, message), 'retried', message);
|
|
152
242
|
}
|
|
153
243
|
// Anything else: retry with backoff until maxAttempts, then fail.
|
|
154
244
|
if (job.attempts + 1 >= job.maxAttempts) {
|
|
155
|
-
return this.fail(job, message);
|
|
245
|
+
return this.fail(job, claim, message);
|
|
156
246
|
}
|
|
157
|
-
|
|
158
|
-
return 'retried';
|
|
247
|
+
const delay = this.backoff(job.attempts, cfg);
|
|
248
|
+
return this.settle(job, await this.store.retry(this.db, claim, delay, message), 'retried', message);
|
|
159
249
|
}
|
|
160
|
-
async fail(job, reason) {
|
|
161
|
-
this.
|
|
162
|
-
|
|
163
|
-
|
|
250
|
+
async fail(job, claim, reason) {
|
|
251
|
+
const applied = await this.store.markFailed(this.db, claim, reason);
|
|
252
|
+
if (applied)
|
|
253
|
+
this.logger.warn(`job ${job.id} ("${job.name}") failed: ${reason}`);
|
|
254
|
+
return this.settle(job, applied, 'failed', reason);
|
|
164
255
|
}
|
|
165
256
|
backoff(attempts, cfg) {
|
|
166
257
|
const base = cfg.baseBackoffMs * 2 ** attempts;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs-claimer.service.js","sourceRoot":"","sources":["../jobs-claimer.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"jobs-claimer.service.js","sourceRoot":"","sources":["../jobs-claimer.service.ts"],"names":[],"mappings":"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"}
|
package/dist/jobs-worker.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunnerConfig } from './interfaces';
|
|
2
|
-
import type
|
|
2
|
+
import { type JobsClaimer, type TickReport } from './jobs-claimer.service';
|
|
3
3
|
export interface WorkerLoopOptions {
|
|
4
4
|
/** Delay between ticks when the last tick claimed nothing (default 2000ms). */
|
|
5
5
|
pollIntervalMs?: number;
|
|
@@ -17,5 +17,9 @@ export interface WorkerLoopOptions {
|
|
|
17
17
|
* batch it loops immediately to drain the backlog; when it claims nothing it
|
|
18
18
|
* waits `pollIntervalMs`. A throwing tick is reported via `onError` and the loop
|
|
19
19
|
* continues after the same wait.
|
|
20
|
+
*
|
|
21
|
+
* An invalid `options.runner` rejects at once, before the first tick: it is a
|
|
22
|
+
* wiring mistake, and every tick would otherwise throw into an optional
|
|
23
|
+
* `onError`. Keep the returned promise and handle that rejection.
|
|
20
24
|
*/
|
|
21
25
|
export declare function runWorkerLoop(claimer: JobsClaimer, options?: WorkerLoopOptions): Promise<void>;
|
package/dist/jobs-worker.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runWorkerLoop = runWorkerLoop;
|
|
4
|
+
const jobs_claimer_service_1 = require("./jobs-claimer.service");
|
|
4
5
|
/**
|
|
5
6
|
* Runs `claimer.tick()` in a loop until `signal` aborts. When a tick claims a
|
|
6
7
|
* batch it loops immediately to drain the backlog; when it claims nothing it
|
|
7
8
|
* waits `pollIntervalMs`. A throwing tick is reported via `onError` and the loop
|
|
8
9
|
* continues after the same wait.
|
|
10
|
+
*
|
|
11
|
+
* An invalid `options.runner` rejects at once, before the first tick: it is a
|
|
12
|
+
* wiring mistake, and every tick would otherwise throw into an optional
|
|
13
|
+
* `onError`. Keep the returned promise and handle that rejection.
|
|
9
14
|
*/
|
|
10
15
|
async function runWorkerLoop(claimer, options = {}) {
|
|
16
|
+
(0, jobs_claimer_service_1.resolveRunnerConfig)(options.runner);
|
|
11
17
|
const pollIntervalMs = options.pollIntervalMs ?? 2_000;
|
|
12
18
|
const { signal } = options;
|
|
13
19
|
while (!signal?.aborted) {
|
package/dist/jobs-worker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs-worker.js","sourceRoot":"","sources":["../jobs-worker.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"jobs-worker.js","sourceRoot":"","sources":["../jobs-worker.ts"],"names":[],"mappings":";;AA8BA,sCAmBC;AAhDD,iEAIgC;AAehC;;;;;;;;;GASG;AACI,KAAK,UAAU,aAAa,CACjC,OAAoB,EACpB,UAA6B,EAAE;IAE/B,IAAA,0CAAmB,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC;IACvD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAClD,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YACzB,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;YACzB,MAAM,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,EAAU,EAAE,MAAoB;IAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtC,MAAM,EAAE,gBAAgB,CACtB,OAAO,EACP,GAAG,EAAE;YACH,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/testing/harness.js
CHANGED
|
@@ -17,7 +17,7 @@ exports.drainJobs = drainJobs;
|
|
|
17
17
|
*/
|
|
18
18
|
async function drainJobs(claimer, options = {}) {
|
|
19
19
|
const maxTicks = options.maxTicks ?? 100;
|
|
20
|
-
const total = { scheduled: 0, claimed: 0, completed: 0, retried: 0, failed: 0 };
|
|
20
|
+
const total = { scheduled: 0, claimed: 0, completed: 0, retried: 0, failed: 0, lost: 0 };
|
|
21
21
|
for (let tick = 0; tick < maxTicks; tick += 1) {
|
|
22
22
|
const report = await claimer.tick(options.runner);
|
|
23
23
|
total.scheduled += report.scheduled;
|
|
@@ -25,6 +25,7 @@ async function drainJobs(claimer, options = {}) {
|
|
|
25
25
|
total.completed += report.completed;
|
|
26
26
|
total.retried += report.retried;
|
|
27
27
|
total.failed += report.failed;
|
|
28
|
+
total.lost += report.lost;
|
|
28
29
|
if (report.claimed === 0) {
|
|
29
30
|
return total;
|
|
30
31
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../testing/harness.ts"],"names":[],"mappings":";;;AA+BA,
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../../testing/harness.ts"],"names":[],"mappings":";;;AA+BA,8BAsBC;AAnCD;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,SAAS,CAC7B,OAAoB,EACpB,UAA4B,EAAE;IAE9B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;IACzC,MAAM,KAAK,GAAe,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrG,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAClD,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;QACpC,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC;QAChC,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;QACpC,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC;QAChC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;QAC9B,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;QAC1B,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,kCAAkC,QAAQ,iCAAiC;QACzE,2DAA2D,CAC9D,CAAC;AACJ,CAAC;AAQD;;;;;;;;;;;;GAYG;AACH,MAAa,mBAAmB;IAAhC;QACmB,YAAO,GAA2B,EAAE,CAAC;QACrC,oBAAe,GAAY,EAAE,CAAC;IAwCjD,CAAC;IArCC,MAAM,CAAC,OAAgC,EAAE,GAAe;QACtD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7C,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,OAAO,CAAC;QAChB,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC;QACrB,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,YAAY,CAAC,KAAY;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,mEAAmE;IACnE,QAAQ,CAAC,KAAY;QACnB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,yDAAyD;IACzD,YAAY;QACV,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,wCAAwC;IACxC,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,wDAAwD;IACxD,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACxB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;CACF;AA1CD,kDA0CC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nest-native/jobs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Background jobs for NestJS without Redis \u2014 a Drizzle-backed job queue (SQLite, Postgres + MySQL) with transactional enqueue, retries, delayed and unique jobs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nestjs",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"croner": "^10.0.1"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"@nestjs-cls/transactional": "^3.0.0",
|
|
81
|
+
"@nestjs-cls/transactional": "^3.0.0 || ^4.0.0",
|
|
82
82
|
"@nestjs/common": "^11.0.0 || ^12.0.0",
|
|
83
83
|
"@nestjs/core": "^11.0.0 || ^12.0.0",
|
|
84
84
|
"better-sqlite3": "^11.0.0 || ^12.0.0 || ^13.0.0",
|