@odunlamizo/node-river 1.0.8 → 1.1.1
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 +91 -37
- package/dist/driver-BhanG0yg.d.cts +70 -0
- package/dist/driver-DQ6PVYVO.d.ts +70 -0
- package/dist/drivers/pg/index.cjs +74 -3
- package/dist/drivers/pg/index.cjs.map +1 -1
- package/dist/drivers/pg/index.d.cts +5 -2
- package/dist/drivers/pg/index.d.ts +5 -2
- package/dist/drivers/pg/index.js +74 -3
- package/dist/drivers/pg/index.js.map +1 -1
- package/dist/index.cjs +107 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -28
- package/dist/index.d.ts +49 -28
- package/dist/index.js +102 -30
- package/dist/index.js.map +1 -1
- package/dist/{insert-result-Bf0bAFvJ.d.cts → insert-result-DWZkRtk9.d.cts} +27 -26
- package/dist/{insert-result-Bf0bAFvJ.d.ts → insert-result-DWZkRtk9.d.ts} +27 -26
- package/dist/types.d.cts +42 -6
- package/dist/types.d.ts +42 -6
- package/package.json +1 -1
- package/dist/driver-okKFbSrB.d.ts +0 -54
- package/dist/driver-vSyPLsFq.d.cts +0 -54
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# node-river
|
|
2
2
|
|
|
3
|
-
Node.js
|
|
3
|
+
Node.js client for [River](https://riverqueue.com), a Postgres-backed job queue. Supports job insertion and worker-based processing with per-queue concurrency control.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,62 +8,49 @@ Node.js library to support River integration.
|
|
|
8
8
|
npm install @odunlamizo/node-river
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
### 1. Setup
|
|
11
|
+
## Setup
|
|
14
12
|
|
|
15
13
|
```ts
|
|
16
|
-
import { RiverClient } from 'node-river';
|
|
17
|
-
import { PgDriver } from 'node-river/drivers/pg';
|
|
14
|
+
import { RiverClient } from '@odunlamizo/node-river';
|
|
15
|
+
import { PgDriver } from '@odunlamizo/node-river/drivers/pg';
|
|
18
16
|
|
|
19
17
|
const driver = new PgDriver({ connectionString: process.env.DATABASE_URL! });
|
|
18
|
+
|
|
20
19
|
const client = new RiverClient(driver, {
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
queues: {
|
|
21
|
+
default: { concurrency: 10 },
|
|
22
|
+
emails: { concurrency: 50 },
|
|
23
|
+
},
|
|
24
|
+
maxAttempts: 3,
|
|
23
25
|
});
|
|
24
|
-
```
|
|
25
26
|
|
|
26
|
-
### 2. Verify Connection
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
27
|
await client.verifyConnection();
|
|
30
28
|
```
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
## Inserting Jobs
|
|
33
31
|
|
|
34
|
-
|
|
35
|
-
const result = await client.insert({ kind: 'sort_args', strings: ['banana', 'apple', 'cherry'] });
|
|
36
|
-
console.log(result.job); // Job details
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### 4. Insert Unique Job
|
|
32
|
+
`queue` is required on every insert — there is no default fallback.
|
|
40
33
|
|
|
41
34
|
```ts
|
|
35
|
+
// Single job
|
|
42
36
|
const result = await client.insert(
|
|
43
|
-
{ kind: '
|
|
44
|
-
{
|
|
37
|
+
{ kind: 'send_email', to: 'user@example.com' },
|
|
38
|
+
{ queue: 'emails' },
|
|
45
39
|
);
|
|
46
|
-
console.log(result.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
### 5. Insert Many Jobs Transactionally
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
const jobs = [
|
|
53
|
-
{ args: { kind: 'sort_args', strings: ['a', 'b'] }, opts: {} },
|
|
54
|
-
{ args: { kind: 'sort_args', strings: ['c', 'd'] }, opts: {} },
|
|
55
|
-
];
|
|
56
|
-
const results = await client.insertMany(jobs);
|
|
57
|
-
console.log(results.length); // 2
|
|
58
|
-
```
|
|
40
|
+
console.log(result.job); // inserted Job record
|
|
41
|
+
console.log(result.skipped); // true if deduplicated
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
// Multiple jobs in one transaction (all succeed or all roll back)
|
|
44
|
+
const results = await client.insertMany([
|
|
45
|
+
{ args: { kind: 'send_email', to: 'a@example.com' }, opts: { queue: 'emails' } },
|
|
46
|
+
{ args: { kind: 'send_email', to: 'b@example.com' }, opts: { queue: 'emails' } },
|
|
47
|
+
]);
|
|
61
48
|
|
|
62
|
-
|
|
49
|
+
// Inside an existing transaction
|
|
63
50
|
const tx = await driver.getTx();
|
|
64
51
|
try {
|
|
65
52
|
await tx.query('BEGIN');
|
|
66
|
-
|
|
53
|
+
await client.insertTx(tx, { kind: 'send_email', to: 'c@example.com' }, { queue: 'emails' });
|
|
67
54
|
await tx.query('COMMIT');
|
|
68
55
|
} catch (e) {
|
|
69
56
|
await tx.query('ROLLBACK');
|
|
@@ -73,6 +60,73 @@ try {
|
|
|
73
60
|
}
|
|
74
61
|
```
|
|
75
62
|
|
|
63
|
+
## Unique Jobs
|
|
64
|
+
|
|
65
|
+
Use `uniqueOpts` to prevent duplicate jobs from being enqueued.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// Deduplicate by specific args
|
|
69
|
+
await client.insert(
|
|
70
|
+
{ kind: 'send_email', to: 'user@example.com' },
|
|
71
|
+
{ queue: 'emails', uniqueOpts: { byArgs: ['to'] } },
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Deduplicate by queue + args
|
|
75
|
+
await client.insert(
|
|
76
|
+
{ kind: 'send_email', to: 'user@example.com' },
|
|
77
|
+
{ queue: 'emails', uniqueOpts: { byQueue: true, byArgs: ['to'] } },
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Deduplicate within a time window (e.g. one per 60-second period)
|
|
81
|
+
await client.insert(
|
|
82
|
+
{ kind: 'send_email', to: 'user@example.com' },
|
|
83
|
+
{ queue: 'emails', uniqueOpts: { byPeriod: 60 }, scheduledAt: new Date() },
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Processing Jobs (Workers)
|
|
88
|
+
|
|
89
|
+
Implement the `Worker<T>` interface for each job kind, register it with `addWorker`, then call `work()`. Each queue is polled independently at the configured concurrency limit.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { Worker, Job } from '@odunlamizo/node-river/types';
|
|
93
|
+
|
|
94
|
+
interface SendEmailArgs {
|
|
95
|
+
kind: 'send_email';
|
|
96
|
+
to: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class SendEmailWorker implements Worker<SendEmailArgs> {
|
|
100
|
+
async work(job: Job<SendEmailArgs>): Promise<void> {
|
|
101
|
+
await sendEmail(job.args.to);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
client.addWorker('send_email', new SendEmailWorker());
|
|
106
|
+
client.work(); // starts polling all configured queues
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Throwing inside `work()` marks the job as failed. If `attempt < maxAttempts` it is retried with exponential backoff; otherwise it is discarded.
|
|
110
|
+
|
|
111
|
+
## Shutdown
|
|
112
|
+
|
|
113
|
+
Always call `close()` on shutdown to drain the poll timer and release database connections.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
process.on('SIGTERM', async () => {
|
|
117
|
+
await client.close();
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
| Option | Type | Description |
|
|
124
|
+
| -------------- | ----------------------------------------- | ------------------------------------------------------------------- |
|
|
125
|
+
| `queues` | `Record<string, { concurrency: number }>` | Queues to poll and their concurrency limits. Required for `work()`. |
|
|
126
|
+
| `maxAttempts` | `number` | Default max attempts for inserted jobs. |
|
|
127
|
+
| `pollInterval` | `number` | Milliseconds between polls. Defaults to `1000`. |
|
|
128
|
+
| `clientId` | `string` | Unique ID for this client instance. Defaults to `hostname-pid`. |
|
|
129
|
+
|
|
76
130
|
## License
|
|
77
131
|
|
|
78
132
|
MIT
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { J as JobArgs, I as InsertOpts, a as InsertResult, b as Job } from './insert-result-DWZkRtk9.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Common interface all River drivers must implement (e.g. PgDriver).
|
|
5
|
+
* `Tx` is the driver-specific transaction type (e.g. `PoolClient` for pg).
|
|
6
|
+
*/
|
|
7
|
+
interface Driver<Tx> {
|
|
8
|
+
/**
|
|
9
|
+
* Verifies the driver can reach the database. Throws if the connection fails.
|
|
10
|
+
*/
|
|
11
|
+
verifyConnection(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Closes all database connections and frees resources.
|
|
14
|
+
*/
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Inserts a single job into the queue.
|
|
18
|
+
* @param args - Job arguments including `kind` and any job-specific fields.
|
|
19
|
+
* @param opts - Insertion options: queue, maxAttempts, priority, tags, etc.
|
|
20
|
+
* @returns The inserted job and a `skipped` flag if deduplicated by uniqueness.
|
|
21
|
+
*/
|
|
22
|
+
insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>>;
|
|
23
|
+
/**
|
|
24
|
+
* Inserts a single job within an existing transaction.
|
|
25
|
+
* @param tx - The active transaction (type is driver-specific).
|
|
26
|
+
* @param args - Job arguments including `kind` and any job-specific fields.
|
|
27
|
+
* @param opts - Insertion options: queue, maxAttempts, priority, tags, etc.
|
|
28
|
+
* @returns The inserted job and a `skipped` flag if deduplicated by uniqueness.
|
|
29
|
+
*/
|
|
30
|
+
insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts): Promise<InsertResult<T>>;
|
|
31
|
+
/**
|
|
32
|
+
* Inserts multiple jobs in a single transaction. Rolls back all if any fail.
|
|
33
|
+
* @param jobs - Array of `{ args, opts }` pairs, one per job.
|
|
34
|
+
* @returns An array of InsertResult objects in the same order as the input.
|
|
35
|
+
*/
|
|
36
|
+
insertMany<T extends JobArgs>(jobs: {
|
|
37
|
+
args: T;
|
|
38
|
+
opts: InsertOpts;
|
|
39
|
+
}[]): Promise<InsertResult<T>[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a new transaction object for use with `insertTx`.
|
|
42
|
+
* Caller is responsible for committing or rolling back.
|
|
43
|
+
*/
|
|
44
|
+
getTx(): Promise<Tx>;
|
|
45
|
+
/**
|
|
46
|
+
* Atomically claims up to `limit` available jobs and marks them as running.
|
|
47
|
+
* Uses `FOR UPDATE SKIP LOCKED` to prevent concurrent workers from double-claiming.
|
|
48
|
+
* @param queue - The queue to fetch jobs from.
|
|
49
|
+
* @param limit - Max number of jobs to claim, based on available concurrency slots.
|
|
50
|
+
* @param clientId - This client's ID, appended to the job's `attemptedBy` list.
|
|
51
|
+
* @returns Array of claimed jobs ready to be worked.
|
|
52
|
+
*/
|
|
53
|
+
getAvailableJobs(queue: string, limit: number, clientId: string): Promise<Job[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Marks a job as `completed` and stamps `finalizedAt`.
|
|
56
|
+
* @param id - The ID of the job to complete.
|
|
57
|
+
*/
|
|
58
|
+
completeJob(id: number): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Marks a job as `retryable` (with exponential backoff) or `discarded` (if max attempts reached).
|
|
61
|
+
* Appends the error details to the job's `errors` array.
|
|
62
|
+
* @param id - The ID of the job that failed.
|
|
63
|
+
* @param error - The error thrown by the worker.
|
|
64
|
+
* @param attempt - The attempt number that failed.
|
|
65
|
+
* @param maxAttempts - The job's maximum allowed attempts.
|
|
66
|
+
*/
|
|
67
|
+
failJob(id: number, error: Error, attempt: number, maxAttempts: number): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type { Driver as D };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { J as JobArgs, I as InsertOpts, a as InsertResult, b as Job } from './insert-result-DWZkRtk9.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Common interface all River drivers must implement (e.g. PgDriver).
|
|
5
|
+
* `Tx` is the driver-specific transaction type (e.g. `PoolClient` for pg).
|
|
6
|
+
*/
|
|
7
|
+
interface Driver<Tx> {
|
|
8
|
+
/**
|
|
9
|
+
* Verifies the driver can reach the database. Throws if the connection fails.
|
|
10
|
+
*/
|
|
11
|
+
verifyConnection(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Closes all database connections and frees resources.
|
|
14
|
+
*/
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Inserts a single job into the queue.
|
|
18
|
+
* @param args - Job arguments including `kind` and any job-specific fields.
|
|
19
|
+
* @param opts - Insertion options: queue, maxAttempts, priority, tags, etc.
|
|
20
|
+
* @returns The inserted job and a `skipped` flag if deduplicated by uniqueness.
|
|
21
|
+
*/
|
|
22
|
+
insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>>;
|
|
23
|
+
/**
|
|
24
|
+
* Inserts a single job within an existing transaction.
|
|
25
|
+
* @param tx - The active transaction (type is driver-specific).
|
|
26
|
+
* @param args - Job arguments including `kind` and any job-specific fields.
|
|
27
|
+
* @param opts - Insertion options: queue, maxAttempts, priority, tags, etc.
|
|
28
|
+
* @returns The inserted job and a `skipped` flag if deduplicated by uniqueness.
|
|
29
|
+
*/
|
|
30
|
+
insertTx<T extends JobArgs>(tx: Tx, args: T, opts: InsertOpts): Promise<InsertResult<T>>;
|
|
31
|
+
/**
|
|
32
|
+
* Inserts multiple jobs in a single transaction. Rolls back all if any fail.
|
|
33
|
+
* @param jobs - Array of `{ args, opts }` pairs, one per job.
|
|
34
|
+
* @returns An array of InsertResult objects in the same order as the input.
|
|
35
|
+
*/
|
|
36
|
+
insertMany<T extends JobArgs>(jobs: {
|
|
37
|
+
args: T;
|
|
38
|
+
opts: InsertOpts;
|
|
39
|
+
}[]): Promise<InsertResult<T>[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a new transaction object for use with `insertTx`.
|
|
42
|
+
* Caller is responsible for committing or rolling back.
|
|
43
|
+
*/
|
|
44
|
+
getTx(): Promise<Tx>;
|
|
45
|
+
/**
|
|
46
|
+
* Atomically claims up to `limit` available jobs and marks them as running.
|
|
47
|
+
* Uses `FOR UPDATE SKIP LOCKED` to prevent concurrent workers from double-claiming.
|
|
48
|
+
* @param queue - The queue to fetch jobs from.
|
|
49
|
+
* @param limit - Max number of jobs to claim, based on available concurrency slots.
|
|
50
|
+
* @param clientId - This client's ID, appended to the job's `attemptedBy` list.
|
|
51
|
+
* @returns Array of claimed jobs ready to be worked.
|
|
52
|
+
*/
|
|
53
|
+
getAvailableJobs(queue: string, limit: number, clientId: string): Promise<Job[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Marks a job as `completed` and stamps `finalizedAt`.
|
|
56
|
+
* @param id - The ID of the job to complete.
|
|
57
|
+
*/
|
|
58
|
+
completeJob(id: number): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Marks a job as `retryable` (with exponential backoff) or `discarded` (if max attempts reached).
|
|
61
|
+
* Appends the error details to the job's `errors` array.
|
|
62
|
+
* @param id - The ID of the job that failed.
|
|
63
|
+
* @param error - The error thrown by the worker.
|
|
64
|
+
* @param attempt - The attempt number that failed.
|
|
65
|
+
* @param maxAttempts - The job's maximum allowed attempts.
|
|
66
|
+
*/
|
|
67
|
+
failJob(id: number, error: Error, attempt: number, maxAttempts: number): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type { Driver as D };
|
|
@@ -98,9 +98,6 @@ var PgDriver = class {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
async insertTx(tx, args, opts) {
|
|
101
|
-
if (!opts.queue) {
|
|
102
|
-
throw new Error("Queue name is required in InsertOpts");
|
|
103
|
-
}
|
|
104
101
|
if (!opts.maxAttempts) {
|
|
105
102
|
throw new Error("maxAttempts is required in InsertOpts");
|
|
106
103
|
}
|
|
@@ -204,6 +201,80 @@ var PgDriver = class {
|
|
|
204
201
|
async getTx() {
|
|
205
202
|
return this.pool.connect();
|
|
206
203
|
}
|
|
204
|
+
async getAvailableJobs(queue, limit, clientId) {
|
|
205
|
+
const result = await this.pool.query(
|
|
206
|
+
`
|
|
207
|
+
UPDATE river_job
|
|
208
|
+
SET state = 'running',
|
|
209
|
+
attempt = attempt + 1,
|
|
210
|
+
attempted_at = NOW(),
|
|
211
|
+
attempted_by = array_append(COALESCE(attempted_by, '{}'), $1)
|
|
212
|
+
WHERE id IN (
|
|
213
|
+
SELECT id FROM river_job
|
|
214
|
+
WHERE state = 'available'
|
|
215
|
+
AND queue = $2
|
|
216
|
+
AND scheduled_at <= NOW()
|
|
217
|
+
ORDER BY priority, scheduled_at, id
|
|
218
|
+
LIMIT $3
|
|
219
|
+
FOR UPDATE SKIP LOCKED
|
|
220
|
+
)
|
|
221
|
+
RETURNING
|
|
222
|
+
id,
|
|
223
|
+
state,
|
|
224
|
+
attempt,
|
|
225
|
+
max_attempts as "maxAttempts",
|
|
226
|
+
attempted_at as "attemptedAt",
|
|
227
|
+
created_at as "createdAt",
|
|
228
|
+
finalized_at as "finalizedAt",
|
|
229
|
+
scheduled_at as "scheduledAt",
|
|
230
|
+
priority,
|
|
231
|
+
args,
|
|
232
|
+
attempted_by as "attemptedBy",
|
|
233
|
+
errors,
|
|
234
|
+
kind,
|
|
235
|
+
metadata,
|
|
236
|
+
queue,
|
|
237
|
+
tags,
|
|
238
|
+
unique_key as "uniqueKey",
|
|
239
|
+
unique_states as "uniqueStates"
|
|
240
|
+
`,
|
|
241
|
+
[clientId, queue, limit]
|
|
242
|
+
);
|
|
243
|
+
return result.rows;
|
|
244
|
+
}
|
|
245
|
+
async completeJob(id) {
|
|
246
|
+
await this.pool.query(
|
|
247
|
+
`
|
|
248
|
+
UPDATE river_job
|
|
249
|
+
SET state = $1,
|
|
250
|
+
finalized_at = NOW()
|
|
251
|
+
WHERE id = $2
|
|
252
|
+
`,
|
|
253
|
+
["completed" /* Completed */, id]
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
async failJob(id, error, attempt, maxAttempts) {
|
|
257
|
+
const isDiscarded = attempt >= maxAttempts;
|
|
258
|
+
const backoffSeconds = Math.pow(2, attempt);
|
|
259
|
+
const errorEntry = JSON.stringify({ error: error.message, attempt });
|
|
260
|
+
await this.pool.query(
|
|
261
|
+
`
|
|
262
|
+
UPDATE river_job
|
|
263
|
+
SET state = $1,
|
|
264
|
+
finalized_at = CASE WHEN $2 THEN NOW() ELSE NULL END,
|
|
265
|
+
scheduled_at = CASE WHEN $2 THEN scheduled_at ELSE NOW() + ($3 || ' seconds')::interval END,
|
|
266
|
+
errors = array_append(COALESCE(errors, '{}'), $4::jsonb)
|
|
267
|
+
WHERE id = $5
|
|
268
|
+
`,
|
|
269
|
+
[
|
|
270
|
+
isDiscarded ? "discarded" /* Discarded */ : "retryable" /* Retryable */,
|
|
271
|
+
isDiscarded,
|
|
272
|
+
backoffSeconds,
|
|
273
|
+
errorEntry,
|
|
274
|
+
id
|
|
275
|
+
]
|
|
276
|
+
);
|
|
277
|
+
}
|
|
207
278
|
/**
|
|
208
279
|
* Helper to map a DB row to a Job<T> and InsertResult.
|
|
209
280
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/mappers.ts","../../../src/drivers/pg/pg-driver.ts"],"names":["crypto","Buffer","Pool","query","values","result","row"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Buffer } from 'buffer';\nimport crypto from 'crypto';\nimport { InsertOpts, JobArgs, JobState } from '../types';\n\n/**\n * Converts a bit(8) Buffer to an array of JobState values using the bit order from river_job_state_in_bitmask.\n *\n * @param bitmask - The Buffer representing the job state bitmask.\n * @returns An array of JobState values corresponding to the set bits, or null if the bitmask is null.\n */\nexport const bitmaskToJobStates = (bitmask: Buffer | null): JobState[] | null => {\n if (!bitmask || bitmask.length === 0) return null;\n // Bit order: 7=available, 6=cancelled, 5=completed, 4=discarded, 3=pending, 2=retryable, 1=running, 0=scheduled\n const allStates: JobState[] = [\n JobState.Available, // 7\n JobState.Cancelled, // 6\n JobState.Completed, // 5\n JobState.Discarded, // 4\n JobState.Pending, // 3\n JobState.Retryable, // 2\n JobState.Running, // 1\n JobState.Scheduled, // 0\n ];\n const byte = bitmask[0];\n // Map bits 7..0 to allStates\n return allStates.filter((_, i) => (byte & (1 << (7 - i))) !== 0);\n};\n\n/**\n * Maps job args and unique options to a unique key buffer.\n *\n * @param args - The job arguments to use for uniqueness computation.\n * @param opts - The insertion options, including unique options.\n * @returns A Buffer containing the unique key, or undefined if no unique options are set.\n */\nexport const mapToUniqueKey = (args: JobArgs, opts: InsertOpts): Buffer | undefined => {\n const uniqueOpts = opts.uniqueOpts;\n if (!uniqueOpts) return undefined;\n\n const keyParts: Record<string, unknown> = {};\n\n if (!uniqueOpts.excludeKind) {\n keyParts.kind = args.kind;\n }\n\n if (uniqueOpts.byArgs === true) {\n keyParts.args = args;\n } else if (Array.isArray(uniqueOpts.byArgs)) {\n keyParts.args = Object.fromEntries(uniqueOpts.byArgs.sort().map((k) => [k, args[k]]));\n }\n\n if (uniqueOpts.byQueue) {\n keyParts.queue = opts.queue;\n }\n\n if (uniqueOpts.byPeriod && opts.scheduledAt) {\n const date = new Date(opts.scheduledAt);\n const period = uniqueOpts.byPeriod;\n const timestamp = Math.floor(date.getTime() / 1000);\n const rounded = timestamp - (timestamp % period);\n keyParts.period = rounded;\n }\n\n const hash = crypto.createHash('sha256').update(JSON.stringify(keyParts)).digest();\n\n return Buffer.from(hash);\n};\n","// RiverQueue driver implementation using the 'pg' library.\nimport { Buffer } from 'buffer';\nimport { Pool, PoolClient, PoolConfig } from 'pg';\nimport { InsertOpts, InsertResult, Job, JobArgs } from '../../types';\nimport { bitmaskToJobStates, mapToUniqueKey } from '../../utils';\nimport Driver from '../driver';\nimport Options from './pg-options';\n\n// Implements the RiverQueue Driver interface using the 'pg' library.\nexport default class PgDriver implements Driver<PoolClient> {\n private readonly pool: Pool;\n\n /**\n * Creates a new PgDriver instance.\n * @param options - Options for configuring the RiverQueue pg driver connection pool. Fields include:\n * - connectionString: Database connection string\n * - connectionTimeoutMillis: Optional, connection timeout in milliseconds\n * - idleTimeoutMillis: Optional, idle timeout in milliseconds\n * - max: Optional, maximum number of clients in the pool\n */\n constructor(options: Options) {\n const config: PoolConfig = { ...options };\n this.pool = new Pool(config);\n }\n\n async verifyConnection(): Promise<void> {\n try {\n const client = await this.pool.connect();\n try {\n await client.query('SELECT 1');\n } finally {\n client.release();\n }\n } catch (error) {\n throw new Error(\n `Database connection failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>> {\n const client = await this.pool.connect();\n try {\n return await this.insertTx(client, args, opts);\n } finally {\n client.release();\n }\n }\n\n async insertTx<T extends JobArgs>(\n tx: PoolClient,\n args: T,\n opts: InsertOpts,\n ): Promise<InsertResult<T>> {\n if (!opts.queue) {\n throw new Error('Queue name is required in InsertOpts');\n }\n\n if (!opts.maxAttempts) {\n throw new Error('maxAttempts is required in InsertOpts');\n }\n\n let uniqueKey: Buffer | undefined;\n if (opts.uniqueOpts) {\n uniqueKey = mapToUniqueKey(args, opts);\n }\n\n if (uniqueKey) {\n const stateList = opts.uniqueOpts?.byState || [];\n\n let query = 'SELECT * FROM river_job WHERE unique_key = $1';\n let values: (Buffer | string[])[] = [uniqueKey];\n\n if (stateList.length > 0) {\n query += ' AND state = ANY($2)';\n values.push(stateList);\n }\n\n query += ' LIMIT 1';\n\n const result = await tx.query<Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null }>(\n query,\n values,\n );\n\n if (result.rows.length > 0) {\n const row = result.rows[0];\n\n return this.mapRowToInsertResult(row, true);\n }\n }\n\n const { kind, ...restArgs } = args;\n const columns = ['kind', 'args', 'queue', 'max_attempts'];\n const values: (string | number | Buffer)[] = [\n kind,\n JSON.stringify(restArgs),\n opts.queue,\n opts.maxAttempts,\n ];\n\n if (opts.tags) {\n columns.push('tags');\n values.push(JSON.stringify(opts.tags));\n }\n\n if (opts.priority) {\n columns.push('priority');\n values.push(opts.priority);\n }\n\n if (opts.metadata) {\n columns.push('metadata');\n values.push(JSON.stringify(opts.metadata));\n }\n\n if (opts.scheduledAt) {\n columns.push('scheduled_at');\n values.push(\n opts.scheduledAt instanceof Date ? opts.scheduledAt.toISOString() : opts.scheduledAt,\n );\n }\n\n if (uniqueKey) {\n columns.push('unique_key');\n values.push(uniqueKey);\n }\n\n const placeholders = columns.map((_, i) => `$${i + 1}`);\n const query = `INSERT INTO river_job (${columns.join(', ')}) VALUES (${placeholders.join(', ')}) RETURNING\n id,\n state,\n attempt,\n max_attempts as \"maxAttempts\",\n attempted_at as \"attemptedAt\",\n created_at as \"createdAt\",\n finalized_at as \"finalizedAt\",\n scheduled_at as \"scheduledAt\",\n priority,\n args,\n attempted_by as \"attemptedBy\",\n errors,\n kind,\n metadata,\n queue,\n tags,\n unique_key as \"uniqueKey\",\n unique_states as \"uniqueStates\"`;\n const result = await tx.query<Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null }>(\n query,\n values,\n );\n\n const row = result.rows[0];\n if (!row) return row;\n\n return this.mapRowToInsertResult(row, false);\n }\n\n async insertMany<T extends JobArgs>(\n jobs: { args: T; opts: InsertOpts }[],\n ): Promise<InsertResult<T>[]> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n const results: InsertResult<T>[] = [];\n for (const job of jobs) {\n results.push(await this.insertTx(client, job.args, job.opts));\n }\n\n await client.query('COMMIT');\n\n return results;\n } catch (error) {\n await client.query('ROLLBACK');\n\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getTx(): Promise<PoolClient> {\n return this.pool.connect();\n }\n\n /**\n * Helper to map a DB row to a Job<T> and InsertResult.\n */\n private mapRowToInsertResult<T extends JobArgs>(\n row: Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null },\n skipped: boolean,\n ): InsertResult<T> {\n return {\n job: {\n ...row,\n args: { ...row.args, kind: row.kind } as T,\n uniqueStates: bitmaskToJobStates(row.uniqueStates),\n },\n skipped,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/mappers.ts","../../../src/drivers/pg/pg-driver.ts"],"names":["crypto","Buffer","Pool","query","values","result","row"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Buffer } from 'buffer';\nimport crypto from 'crypto';\nimport { InsertOpts, JobArgs, JobState } from '../types';\n\n/**\n * Converts a bit(8) Buffer to an array of JobState values using the bit order from river_job_state_in_bitmask.\n *\n * @param bitmask - The Buffer representing the job state bitmask.\n * @returns An array of JobState values corresponding to the set bits, or null if the bitmask is null.\n */\nexport const bitmaskToJobStates = (bitmask: Buffer | null): JobState[] | null => {\n if (!bitmask || bitmask.length === 0) return null;\n // Bit order: 7=available, 6=cancelled, 5=completed, 4=discarded, 3=pending, 2=retryable, 1=running, 0=scheduled\n const allStates: JobState[] = [\n JobState.Available, // 7\n JobState.Cancelled, // 6\n JobState.Completed, // 5\n JobState.Discarded, // 4\n JobState.Pending, // 3\n JobState.Retryable, // 2\n JobState.Running, // 1\n JobState.Scheduled, // 0\n ];\n const byte = bitmask[0];\n // Map bits 7..0 to allStates\n return allStates.filter((_, i) => (byte & (1 << (7 - i))) !== 0);\n};\n\n/**\n * Maps job args and unique options to a unique key buffer.\n *\n * @param args - The job arguments to use for uniqueness computation.\n * @param opts - The insertion options, including unique options.\n * @returns A Buffer containing the unique key, or undefined if no unique options are set.\n */\nexport const mapToUniqueKey = (args: JobArgs, opts: InsertOpts): Buffer | undefined => {\n const uniqueOpts = opts.uniqueOpts;\n if (!uniqueOpts) return undefined;\n\n const keyParts: Record<string, unknown> = {};\n\n if (!uniqueOpts.excludeKind) {\n keyParts.kind = args.kind;\n }\n\n if (uniqueOpts.byArgs === true) {\n keyParts.args = args;\n } else if (Array.isArray(uniqueOpts.byArgs)) {\n keyParts.args = Object.fromEntries(uniqueOpts.byArgs.sort().map((k) => [k, args[k]]));\n }\n\n if (uniqueOpts.byQueue) {\n keyParts.queue = opts.queue;\n }\n\n if (uniqueOpts.byPeriod && opts.scheduledAt) {\n const date = new Date(opts.scheduledAt);\n const period = uniqueOpts.byPeriod;\n const timestamp = Math.floor(date.getTime() / 1000);\n const rounded = timestamp - (timestamp % period);\n keyParts.period = rounded;\n }\n\n const hash = crypto.createHash('sha256').update(JSON.stringify(keyParts)).digest();\n\n return Buffer.from(hash);\n};\n","// RiverQueue driver implementation using the 'pg' library.\nimport { Buffer } from 'buffer';\nimport { Pool, PoolClient, PoolConfig } from 'pg';\nimport { InsertOpts, InsertResult, Job, JobArgs, JobState } from '../../types';\nimport { bitmaskToJobStates, mapToUniqueKey } from '../../utils';\nimport Driver from '../driver';\nimport Options from './pg-options';\n\n// Implements the RiverQueue Driver interface using the 'pg' library.\nexport default class PgDriver implements Driver<PoolClient> {\n private readonly pool: Pool;\n\n /**\n * Creates a new PgDriver instance.\n * @param options - Options for configuring the RiverQueue pg driver connection pool. Fields include:\n * - connectionString: Database connection string\n * - connectionTimeoutMillis: Optional, connection timeout in milliseconds\n * - idleTimeoutMillis: Optional, idle timeout in milliseconds\n * - max: Optional, maximum number of clients in the pool\n */\n constructor(options: Options) {\n const config: PoolConfig = { ...options };\n this.pool = new Pool(config);\n }\n\n async verifyConnection(): Promise<void> {\n try {\n const client = await this.pool.connect();\n try {\n await client.query('SELECT 1');\n } finally {\n client.release();\n }\n } catch (error) {\n throw new Error(\n `Database connection failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n\n async insert<T extends JobArgs>(args: T, opts: InsertOpts): Promise<InsertResult<T>> {\n const client = await this.pool.connect();\n try {\n return await this.insertTx(client, args, opts);\n } finally {\n client.release();\n }\n }\n\n async insertTx<T extends JobArgs>(\n tx: PoolClient,\n args: T,\n opts: InsertOpts,\n ): Promise<InsertResult<T>> {\n if (!opts.maxAttempts) {\n throw new Error('maxAttempts is required in InsertOpts');\n }\n\n let uniqueKey: Buffer | undefined;\n if (opts.uniqueOpts) {\n uniqueKey = mapToUniqueKey(args, opts);\n }\n\n if (uniqueKey) {\n const stateList = opts.uniqueOpts?.byState || [];\n\n let query = 'SELECT * FROM river_job WHERE unique_key = $1';\n let values: (Buffer | string[])[] = [uniqueKey];\n\n if (stateList.length > 0) {\n query += ' AND state = ANY($2)';\n values.push(stateList);\n }\n\n query += ' LIMIT 1';\n\n const result = await tx.query<Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null }>(\n query,\n values,\n );\n\n if (result.rows.length > 0) {\n const row = result.rows[0];\n\n return this.mapRowToInsertResult(row, true);\n }\n }\n\n const { kind, ...restArgs } = args;\n const columns = ['kind', 'args', 'queue', 'max_attempts'];\n const values: (string | number | Buffer)[] = [\n kind,\n JSON.stringify(restArgs),\n opts.queue,\n opts.maxAttempts,\n ];\n\n if (opts.tags) {\n columns.push('tags');\n values.push(JSON.stringify(opts.tags));\n }\n\n if (opts.priority) {\n columns.push('priority');\n values.push(opts.priority);\n }\n\n if (opts.metadata) {\n columns.push('metadata');\n values.push(JSON.stringify(opts.metadata));\n }\n\n if (opts.scheduledAt) {\n columns.push('scheduled_at');\n values.push(\n opts.scheduledAt instanceof Date ? opts.scheduledAt.toISOString() : opts.scheduledAt,\n );\n }\n\n if (uniqueKey) {\n columns.push('unique_key');\n values.push(uniqueKey);\n }\n\n const placeholders = columns.map((_, i) => `$${i + 1}`);\n const query = `INSERT INTO river_job (${columns.join(', ')}) VALUES (${placeholders.join(', ')}) RETURNING\n id,\n state,\n attempt,\n max_attempts as \"maxAttempts\",\n attempted_at as \"attemptedAt\",\n created_at as \"createdAt\",\n finalized_at as \"finalizedAt\",\n scheduled_at as \"scheduledAt\",\n priority,\n args,\n attempted_by as \"attemptedBy\",\n errors,\n kind,\n metadata,\n queue,\n tags,\n unique_key as \"uniqueKey\",\n unique_states as \"uniqueStates\"`;\n const result = await tx.query<Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null }>(\n query,\n values,\n );\n\n const row = result.rows[0];\n if (!row) return row;\n\n return this.mapRowToInsertResult(row, false);\n }\n\n async insertMany<T extends JobArgs>(\n jobs: { args: T; opts: InsertOpts }[],\n ): Promise<InsertResult<T>[]> {\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n\n const results: InsertResult<T>[] = [];\n for (const job of jobs) {\n results.push(await this.insertTx(client, job.args, job.opts));\n }\n\n await client.query('COMMIT');\n\n return results;\n } catch (error) {\n await client.query('ROLLBACK');\n\n throw error;\n } finally {\n client.release();\n }\n }\n\n async getTx(): Promise<PoolClient> {\n return this.pool.connect();\n }\n\n async getAvailableJobs(queue: string, limit: number, clientId: string): Promise<Job[]> {\n const result = await this.pool.query<Job>(\n `\n UPDATE river_job\n SET state = 'running',\n attempt = attempt + 1,\n attempted_at = NOW(),\n attempted_by = array_append(COALESCE(attempted_by, '{}'), $1)\n WHERE id IN (\n SELECT id FROM river_job\n WHERE state = 'available'\n AND queue = $2\n AND scheduled_at <= NOW()\n ORDER BY priority, scheduled_at, id\n LIMIT $3\n FOR UPDATE SKIP LOCKED\n )\n RETURNING\n id,\n state,\n attempt,\n max_attempts as \"maxAttempts\",\n attempted_at as \"attemptedAt\",\n created_at as \"createdAt\",\n finalized_at as \"finalizedAt\",\n scheduled_at as \"scheduledAt\",\n priority,\n args,\n attempted_by as \"attemptedBy\",\n errors,\n kind,\n metadata,\n queue,\n tags,\n unique_key as \"uniqueKey\",\n unique_states as \"uniqueStates\"\n `,\n [clientId, queue, limit],\n );\n\n return result.rows;\n }\n\n async completeJob(id: number): Promise<void> {\n await this.pool.query(\n `\n UPDATE river_job\n SET state = $1,\n finalized_at = NOW()\n WHERE id = $2\n `,\n [JobState.Completed, id],\n );\n }\n\n async failJob(id: number, error: Error, attempt: number, maxAttempts: number): Promise<void> {\n const isDiscarded = attempt >= maxAttempts;\n const backoffSeconds = Math.pow(2, attempt);\n const errorEntry = JSON.stringify({ error: error.message, attempt });\n\n await this.pool.query(\n `\n UPDATE river_job\n SET state = $1,\n finalized_at = CASE WHEN $2 THEN NOW() ELSE NULL END,\n scheduled_at = CASE WHEN $2 THEN scheduled_at ELSE NOW() + ($3 || ' seconds')::interval END,\n errors = array_append(COALESCE(errors, '{}'), $4::jsonb)\n WHERE id = $5\n `,\n [\n isDiscarded ? JobState.Discarded : JobState.Retryable,\n isDiscarded,\n backoffSeconds,\n errorEntry,\n id,\n ],\n );\n }\n\n /**\n * Helper to map a DB row to a Job<T> and InsertResult.\n */\n private mapRowToInsertResult<T extends JobArgs>(\n row: Omit<Job, 'uniqueStates'> & { uniqueStates: Buffer | null },\n skipped: boolean,\n ): InsertResult<T> {\n return {\n job: {\n ...row,\n args: { ...row.args, kind: row.kind } as T,\n uniqueStates: bitmaskToJobStates(row.uniqueStates),\n },\n skipped,\n };\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PoolClient } from 'pg';
|
|
2
|
-
import { J as JobArgs, I as InsertOpts, a as InsertResult } from '../../insert-result-
|
|
3
|
-
import { D as Driver } from '../../driver-
|
|
2
|
+
import { J as JobArgs, I as InsertOpts, a as InsertResult, b as Job } from '../../insert-result-DWZkRtk9.cjs';
|
|
3
|
+
import { D as Driver } from '../../driver-BhanG0yg.cjs';
|
|
4
4
|
import 'buffer';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -37,6 +37,9 @@ declare class PgDriver implements Driver<PoolClient> {
|
|
|
37
37
|
opts: InsertOpts;
|
|
38
38
|
}[]): Promise<InsertResult<T>[]>;
|
|
39
39
|
getTx(): Promise<PoolClient>;
|
|
40
|
+
getAvailableJobs(queue: string, limit: number, clientId: string): Promise<Job[]>;
|
|
41
|
+
completeJob(id: number): Promise<void>;
|
|
42
|
+
failJob(id: number, error: Error, attempt: number, maxAttempts: number): Promise<void>;
|
|
40
43
|
/**
|
|
41
44
|
* Helper to map a DB row to a Job<T> and InsertResult.
|
|
42
45
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PoolClient } from 'pg';
|
|
2
|
-
import { J as JobArgs, I as InsertOpts, a as InsertResult } from '../../insert-result-
|
|
3
|
-
import { D as Driver } from '../../driver-
|
|
2
|
+
import { J as JobArgs, I as InsertOpts, a as InsertResult, b as Job } from '../../insert-result-DWZkRtk9.js';
|
|
3
|
+
import { D as Driver } from '../../driver-DQ6PVYVO.js';
|
|
4
4
|
import 'buffer';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -37,6 +37,9 @@ declare class PgDriver implements Driver<PoolClient> {
|
|
|
37
37
|
opts: InsertOpts;
|
|
38
38
|
}[]): Promise<InsertResult<T>[]>;
|
|
39
39
|
getTx(): Promise<PoolClient>;
|
|
40
|
+
getAvailableJobs(queue: string, limit: number, clientId: string): Promise<Job[]>;
|
|
41
|
+
completeJob(id: number): Promise<void>;
|
|
42
|
+
failJob(id: number, error: Error, attempt: number, maxAttempts: number): Promise<void>;
|
|
40
43
|
/**
|
|
41
44
|
* Helper to map a DB row to a Job<T> and InsertResult.
|
|
42
45
|
*/
|
package/dist/drivers/pg/index.js
CHANGED
|
@@ -92,9 +92,6 @@ var PgDriver = class {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
async insertTx(tx, args, opts) {
|
|
95
|
-
if (!opts.queue) {
|
|
96
|
-
throw new Error("Queue name is required in InsertOpts");
|
|
97
|
-
}
|
|
98
95
|
if (!opts.maxAttempts) {
|
|
99
96
|
throw new Error("maxAttempts is required in InsertOpts");
|
|
100
97
|
}
|
|
@@ -198,6 +195,80 @@ var PgDriver = class {
|
|
|
198
195
|
async getTx() {
|
|
199
196
|
return this.pool.connect();
|
|
200
197
|
}
|
|
198
|
+
async getAvailableJobs(queue, limit, clientId) {
|
|
199
|
+
const result = await this.pool.query(
|
|
200
|
+
`
|
|
201
|
+
UPDATE river_job
|
|
202
|
+
SET state = 'running',
|
|
203
|
+
attempt = attempt + 1,
|
|
204
|
+
attempted_at = NOW(),
|
|
205
|
+
attempted_by = array_append(COALESCE(attempted_by, '{}'), $1)
|
|
206
|
+
WHERE id IN (
|
|
207
|
+
SELECT id FROM river_job
|
|
208
|
+
WHERE state = 'available'
|
|
209
|
+
AND queue = $2
|
|
210
|
+
AND scheduled_at <= NOW()
|
|
211
|
+
ORDER BY priority, scheduled_at, id
|
|
212
|
+
LIMIT $3
|
|
213
|
+
FOR UPDATE SKIP LOCKED
|
|
214
|
+
)
|
|
215
|
+
RETURNING
|
|
216
|
+
id,
|
|
217
|
+
state,
|
|
218
|
+
attempt,
|
|
219
|
+
max_attempts as "maxAttempts",
|
|
220
|
+
attempted_at as "attemptedAt",
|
|
221
|
+
created_at as "createdAt",
|
|
222
|
+
finalized_at as "finalizedAt",
|
|
223
|
+
scheduled_at as "scheduledAt",
|
|
224
|
+
priority,
|
|
225
|
+
args,
|
|
226
|
+
attempted_by as "attemptedBy",
|
|
227
|
+
errors,
|
|
228
|
+
kind,
|
|
229
|
+
metadata,
|
|
230
|
+
queue,
|
|
231
|
+
tags,
|
|
232
|
+
unique_key as "uniqueKey",
|
|
233
|
+
unique_states as "uniqueStates"
|
|
234
|
+
`,
|
|
235
|
+
[clientId, queue, limit]
|
|
236
|
+
);
|
|
237
|
+
return result.rows;
|
|
238
|
+
}
|
|
239
|
+
async completeJob(id) {
|
|
240
|
+
await this.pool.query(
|
|
241
|
+
`
|
|
242
|
+
UPDATE river_job
|
|
243
|
+
SET state = $1,
|
|
244
|
+
finalized_at = NOW()
|
|
245
|
+
WHERE id = $2
|
|
246
|
+
`,
|
|
247
|
+
["completed" /* Completed */, id]
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
async failJob(id, error, attempt, maxAttempts) {
|
|
251
|
+
const isDiscarded = attempt >= maxAttempts;
|
|
252
|
+
const backoffSeconds = Math.pow(2, attempt);
|
|
253
|
+
const errorEntry = JSON.stringify({ error: error.message, attempt });
|
|
254
|
+
await this.pool.query(
|
|
255
|
+
`
|
|
256
|
+
UPDATE river_job
|
|
257
|
+
SET state = $1,
|
|
258
|
+
finalized_at = CASE WHEN $2 THEN NOW() ELSE NULL END,
|
|
259
|
+
scheduled_at = CASE WHEN $2 THEN scheduled_at ELSE NOW() + ($3 || ' seconds')::interval END,
|
|
260
|
+
errors = array_append(COALESCE(errors, '{}'), $4::jsonb)
|
|
261
|
+
WHERE id = $5
|
|
262
|
+
`,
|
|
263
|
+
[
|
|
264
|
+
isDiscarded ? "discarded" /* Discarded */ : "retryable" /* Retryable */,
|
|
265
|
+
isDiscarded,
|
|
266
|
+
backoffSeconds,
|
|
267
|
+
errorEntry,
|
|
268
|
+
id
|
|
269
|
+
]
|
|
270
|
+
);
|
|
271
|
+
}
|
|
201
272
|
/**
|
|
202
273
|
* Helper to map a DB row to a Job<T> and InsertResult.
|
|
203
274
|
*/
|