@nicnocquee/dataqueue 1.24.0 → 1.25.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 +44 -0
- package/dist/index.cjs +2754 -972
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +440 -12
- package/dist/index.d.ts +440 -12
- package/dist/index.js +2752 -973
- package/dist/index.js.map +1 -1
- package/migrations/1751131910825_add_timeout_seconds_to_job_queue.sql +2 -2
- package/migrations/1751186053000_add_job_events_table.sql +12 -8
- package/migrations/1751984773000_add_tags_to_job_queue.sql +1 -1
- package/migrations/1765809419000_add_force_kill_on_timeout_to_job_queue.sql +1 -1
- package/migrations/1771100000000_add_idempotency_key_to_job_queue.sql +7 -0
- package/migrations/1781200000000_add_wait_support.sql +12 -0
- package/migrations/1781200000001_create_waitpoints_table.sql +18 -0
- package/migrations/1781200000002_add_performance_indexes.sql +34 -0
- package/migrations/1781200000003_add_progress_to_job_queue.sql +7 -0
- package/package.json +20 -6
- package/src/backend.ts +163 -0
- package/src/backends/postgres.ts +1111 -0
- package/src/backends/redis-scripts.ts +533 -0
- package/src/backends/redis.test.ts +543 -0
- package/src/backends/redis.ts +834 -0
- package/src/db-util.ts +4 -2
- package/src/index.test.ts +6 -1
- package/src/index.ts +99 -36
- package/src/processor.test.ts +559 -18
- package/src/processor.ts +512 -44
- package/src/queue.test.ts +217 -6
- package/src/queue.ts +311 -902
- package/src/test-util.ts +32 -0
- package/src/types.ts +349 -16
- package/src/wait.test.ts +698 -0
package/src/db-util.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
|
-
import {
|
|
2
|
+
import { PostgresJobQueueConfig } from './types.js';
|
|
3
3
|
import { parse } from 'pg-connection-string';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
|
|
@@ -26,7 +26,9 @@ function loadPemOrFile(value?: string): string | undefined {
|
|
|
26
26
|
* rejectUnauthorized: true
|
|
27
27
|
* }
|
|
28
28
|
*/
|
|
29
|
-
export const createPool = (
|
|
29
|
+
export const createPool = (
|
|
30
|
+
config: PostgresJobQueueConfig['databaseConfig'],
|
|
31
|
+
): Pool => {
|
|
30
32
|
let searchPath: string | undefined;
|
|
31
33
|
let ssl: any = undefined;
|
|
32
34
|
let customCA: string | undefined;
|
package/src/index.test.ts
CHANGED
|
@@ -81,7 +81,11 @@ describe('index integration', () => {
|
|
|
81
81
|
await new Promise((r) => setTimeout(r, 300));
|
|
82
82
|
processor.stop();
|
|
83
83
|
const job = await jobQueue.getJob(jobId);
|
|
84
|
-
expect(handler).toHaveBeenCalledWith(
|
|
84
|
+
expect(handler).toHaveBeenCalledWith(
|
|
85
|
+
{ foo: 'bar' },
|
|
86
|
+
expect.any(Object),
|
|
87
|
+
expect.any(Object),
|
|
88
|
+
);
|
|
85
89
|
expect(job?.status).toBe('completed');
|
|
86
90
|
});
|
|
87
91
|
|
|
@@ -434,6 +438,7 @@ describe('index integration', () => {
|
|
|
434
438
|
expect(handler).toHaveBeenCalledWith(
|
|
435
439
|
{ foo: 'updated@example.com' },
|
|
436
440
|
expect.any(Object),
|
|
441
|
+
expect.any(Object),
|
|
437
442
|
);
|
|
438
443
|
const job = await jobQueue.getJob(jobId);
|
|
439
444
|
expect(job?.status).toBe('completed');
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
cleanupOldJobs,
|
|
7
|
-
cancelJob,
|
|
8
|
-
cancelAllUpcomingJobs,
|
|
9
|
-
getAllJobs,
|
|
10
|
-
reclaimStuckJobs,
|
|
11
|
-
getJobEvents,
|
|
12
|
-
getJobsByTags,
|
|
13
|
-
getJobs,
|
|
14
|
-
editJob,
|
|
15
|
-
editAllPendingJobs,
|
|
2
|
+
createWaitpoint,
|
|
3
|
+
completeWaitpoint,
|
|
4
|
+
getWaitpoint,
|
|
5
|
+
expireTimedOutWaitpoints,
|
|
16
6
|
} from './queue.js';
|
|
17
7
|
import { createProcessor } from './processor.js';
|
|
18
8
|
import {
|
|
@@ -22,42 +12,70 @@ import {
|
|
|
22
12
|
ProcessorOptions,
|
|
23
13
|
JobHandlers,
|
|
24
14
|
JobType,
|
|
15
|
+
PostgresJobQueueConfig,
|
|
16
|
+
RedisJobQueueConfig,
|
|
25
17
|
} from './types.js';
|
|
18
|
+
import { QueueBackend } from './backend.js';
|
|
26
19
|
import { setLogContext } from './log-context.js';
|
|
27
20
|
import { createPool } from './db-util.js';
|
|
21
|
+
import { PostgresBackend } from './backends/postgres.js';
|
|
22
|
+
import { RedisBackend } from './backends/redis.js';
|
|
28
23
|
|
|
29
24
|
/**
|
|
30
|
-
* Initialize the job queue system
|
|
25
|
+
* Initialize the job queue system.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to PostgreSQL when `backend` is omitted.
|
|
31
28
|
*/
|
|
32
29
|
export const initJobQueue = <PayloadMap = any>(
|
|
33
30
|
config: JobQueueConfig,
|
|
34
31
|
): JobQueue<PayloadMap> => {
|
|
35
|
-
const
|
|
32
|
+
const backendType = config.backend ?? 'postgres';
|
|
33
|
+
setLogContext(config.verbose ?? false);
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
let backend: QueueBackend;
|
|
36
|
+
let pool: import('pg').Pool | undefined;
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
if (backendType === 'postgres') {
|
|
39
|
+
const pgConfig = config as PostgresJobQueueConfig;
|
|
40
|
+
pool = createPool(pgConfig.databaseConfig);
|
|
41
|
+
backend = new PostgresBackend(pool);
|
|
42
|
+
} else if (backendType === 'redis') {
|
|
43
|
+
const redisConfig = (config as RedisJobQueueConfig).redisConfig;
|
|
44
|
+
// RedisBackend constructor will throw if ioredis is not installed
|
|
45
|
+
backend = new RedisBackend(redisConfig);
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error(`Unknown backend: ${backendType}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requirePool = () => {
|
|
51
|
+
if (!pool) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'Wait/Token features require the PostgreSQL backend. Configure with backend: "postgres" to use these features.',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return pool;
|
|
57
|
+
};
|
|
41
58
|
|
|
42
59
|
// Return the job queue API
|
|
43
60
|
return {
|
|
44
61
|
// Job queue operations
|
|
45
62
|
addJob: withLogContext(
|
|
46
|
-
(job: JobOptions<PayloadMap, any>) =>
|
|
63
|
+
(job: JobOptions<PayloadMap, any>) =>
|
|
64
|
+
backend.addJob<PayloadMap, any>(job),
|
|
47
65
|
config.verbose ?? false,
|
|
48
66
|
),
|
|
49
67
|
getJob: withLogContext(
|
|
50
|
-
(id: number) => getJob<PayloadMap, any>(
|
|
68
|
+
(id: number) => backend.getJob<PayloadMap, any>(id),
|
|
51
69
|
config.verbose ?? false,
|
|
52
70
|
),
|
|
53
71
|
getJobsByStatus: withLogContext(
|
|
54
72
|
(status: string, limit?: number, offset?: number) =>
|
|
55
|
-
getJobsByStatus<PayloadMap, any>(
|
|
73
|
+
backend.getJobsByStatus<PayloadMap, any>(status, limit, offset),
|
|
56
74
|
config.verbose ?? false,
|
|
57
75
|
),
|
|
58
76
|
getAllJobs: withLogContext(
|
|
59
77
|
(limit?: number, offset?: number) =>
|
|
60
|
-
getAllJobs<PayloadMap, any>(
|
|
78
|
+
backend.getAllJobs<PayloadMap, any>(limit, offset),
|
|
61
79
|
config.verbose ?? false,
|
|
62
80
|
),
|
|
63
81
|
getJobs: withLogContext(
|
|
@@ -72,20 +90,22 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
72
90
|
},
|
|
73
91
|
limit?: number,
|
|
74
92
|
offset?: number,
|
|
75
|
-
) => getJobs<PayloadMap, any>(
|
|
93
|
+
) => backend.getJobs<PayloadMap, any>(filters, limit, offset),
|
|
76
94
|
config.verbose ?? false,
|
|
77
95
|
),
|
|
78
|
-
retryJob: (jobId: number) => retryJob(
|
|
79
|
-
cleanupOldJobs: (daysToKeep?: number) => cleanupOldJobs(
|
|
96
|
+
retryJob: (jobId: number) => backend.retryJob(jobId),
|
|
97
|
+
cleanupOldJobs: (daysToKeep?: number) => backend.cleanupOldJobs(daysToKeep),
|
|
98
|
+
cleanupOldJobEvents: (daysToKeep?: number) =>
|
|
99
|
+
backend.cleanupOldJobEvents(daysToKeep),
|
|
80
100
|
cancelJob: withLogContext(
|
|
81
|
-
(jobId: number) => cancelJob(
|
|
101
|
+
(jobId: number) => backend.cancelJob(jobId),
|
|
82
102
|
config.verbose ?? false,
|
|
83
103
|
),
|
|
84
104
|
editJob: withLogContext(
|
|
85
105
|
<T extends JobType<PayloadMap>>(
|
|
86
106
|
jobId: number,
|
|
87
107
|
updates: import('./types.js').EditJobOptions<PayloadMap, T>,
|
|
88
|
-
) => editJob
|
|
108
|
+
) => backend.editJob(jobId, updates as import('./backend.js').JobUpdates),
|
|
89
109
|
config.verbose ?? false,
|
|
90
110
|
),
|
|
91
111
|
editAllPendingJobs: withLogContext(
|
|
@@ -104,7 +124,11 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
104
124
|
}
|
|
105
125
|
| undefined,
|
|
106
126
|
updates: import('./types.js').EditJobOptions<PayloadMap, T>,
|
|
107
|
-
) =>
|
|
127
|
+
) =>
|
|
128
|
+
backend.editAllPendingJobs(
|
|
129
|
+
filters,
|
|
130
|
+
updates as import('./backend.js').JobUpdates,
|
|
131
|
+
),
|
|
108
132
|
config.verbose ?? false,
|
|
109
133
|
),
|
|
110
134
|
cancelAllUpcomingJobs: withLogContext(
|
|
@@ -115,17 +139,17 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
115
139
|
| Date
|
|
116
140
|
| { gt?: Date; gte?: Date; lt?: Date; lte?: Date; eq?: Date };
|
|
117
141
|
tags?: { values: string[]; mode?: import('./types.js').TagQueryMode };
|
|
118
|
-
}) => cancelAllUpcomingJobs(
|
|
142
|
+
}) => backend.cancelAllUpcomingJobs(filters),
|
|
119
143
|
config.verbose ?? false,
|
|
120
144
|
),
|
|
121
145
|
reclaimStuckJobs: withLogContext(
|
|
122
146
|
(maxProcessingTimeMinutes?: number) =>
|
|
123
|
-
reclaimStuckJobs(
|
|
147
|
+
backend.reclaimStuckJobs(maxProcessingTimeMinutes),
|
|
124
148
|
config.verbose ?? false,
|
|
125
149
|
),
|
|
126
150
|
getJobsByTags: withLogContext(
|
|
127
151
|
(tags: string[], mode = 'all', limit?: number, offset?: number) =>
|
|
128
|
-
getJobsByTags<PayloadMap, any>(
|
|
152
|
+
backend.getJobsByTags<PayloadMap, any>(tags, mode, limit, offset),
|
|
129
153
|
config.verbose ?? false,
|
|
130
154
|
),
|
|
131
155
|
|
|
@@ -133,14 +157,51 @@ export const initJobQueue = <PayloadMap = any>(
|
|
|
133
157
|
createProcessor: (
|
|
134
158
|
handlers: JobHandlers<PayloadMap>,
|
|
135
159
|
options?: ProcessorOptions,
|
|
136
|
-
) => createProcessor<PayloadMap>(
|
|
137
|
-
|
|
138
|
-
getPool: () => pool,
|
|
160
|
+
) => createProcessor<PayloadMap>(backend, handlers, options),
|
|
161
|
+
|
|
139
162
|
// Job events
|
|
140
163
|
getJobEvents: withLogContext(
|
|
141
|
-
(jobId: number) => getJobEvents(
|
|
164
|
+
(jobId: number) => backend.getJobEvents(jobId),
|
|
165
|
+
config.verbose ?? false,
|
|
166
|
+
),
|
|
167
|
+
|
|
168
|
+
// Wait / Token support (PostgreSQL-only for now)
|
|
169
|
+
createToken: withLogContext(
|
|
170
|
+
(options?: import('./types.js').CreateTokenOptions) =>
|
|
171
|
+
createWaitpoint(requirePool(), null, options),
|
|
142
172
|
config.verbose ?? false,
|
|
143
173
|
),
|
|
174
|
+
completeToken: withLogContext(
|
|
175
|
+
(tokenId: string, data?: any) =>
|
|
176
|
+
completeWaitpoint(requirePool(), tokenId, data),
|
|
177
|
+
config.verbose ?? false,
|
|
178
|
+
),
|
|
179
|
+
getToken: withLogContext(
|
|
180
|
+
(tokenId: string) => getWaitpoint(requirePool(), tokenId),
|
|
181
|
+
config.verbose ?? false,
|
|
182
|
+
),
|
|
183
|
+
expireTimedOutTokens: withLogContext(
|
|
184
|
+
() => expireTimedOutWaitpoints(requirePool()),
|
|
185
|
+
config.verbose ?? false,
|
|
186
|
+
),
|
|
187
|
+
|
|
188
|
+
// Advanced access
|
|
189
|
+
getPool: () => {
|
|
190
|
+
if (backendType !== 'postgres') {
|
|
191
|
+
throw new Error(
|
|
192
|
+
'getPool() is only available with the PostgreSQL backend.',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return (backend as PostgresBackend).getPool();
|
|
196
|
+
},
|
|
197
|
+
getRedisClient: () => {
|
|
198
|
+
if (backendType !== 'redis') {
|
|
199
|
+
throw new Error(
|
|
200
|
+
'getRedisClient() is only available with the Redis backend.',
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return (backend as RedisBackend).getClient();
|
|
204
|
+
},
|
|
144
205
|
};
|
|
145
206
|
};
|
|
146
207
|
|
|
@@ -152,6 +213,8 @@ const withLogContext =
|
|
|
152
213
|
};
|
|
153
214
|
|
|
154
215
|
export * from './types.js';
|
|
216
|
+
export { QueueBackend } from './backend.js';
|
|
217
|
+
export { PostgresBackend } from './backends/postgres.js';
|
|
155
218
|
export {
|
|
156
219
|
validateHandlerSerializable,
|
|
157
220
|
testHandlerSerialization,
|