@elysiumjs/heracles 0.4.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 +321 -0
- package/USAGE.md +527 -0
- package/index.ts +16 -0
- package/package.json +43 -0
- package/src/heracles.ts +68 -0
- package/src/index.ts +28 -0
- package/src/job.ts +413 -0
- package/src/queue.ts +567 -0
- package/src/transport.ts +518 -0
- package/src/transports/redis.transport.ts +1558 -0
- package/src/utils.ts +61 -0
- package/src/worker.ts +312 -0
- package/src/workers/base.worker.ts +804 -0
- package/src/workers/redis.worker.ts +523 -0
- package/tsconfig.json +47 -0
- package/types.d.ts +21 -0
package/src/queue.ts
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
import type { EventHandler } from '@elysiumjs/core';
|
|
16
|
+
import type { JobClass, JobOverlapBehavior } from './job';
|
|
17
|
+
import type { JobStatusInfo, Transport, TransportClass, TransportEvent } from './transport';
|
|
18
|
+
|
|
19
|
+
import { Event, InteractsWithConsole } from '@elysiumjs/core';
|
|
20
|
+
import { map, uid } from 'radash';
|
|
21
|
+
|
|
22
|
+
import { Job, JobStatus } from './job';
|
|
23
|
+
import { TransportMode } from './transport';
|
|
24
|
+
import { ThreadTransport } from './transports/thread.transport';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Options for dispatching a job to a queue.
|
|
28
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
29
|
+
*/
|
|
30
|
+
export type JobDispatchOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* Schedule the job to run at a specific time.
|
|
33
|
+
*/
|
|
34
|
+
scheduledFor?: Date;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Priority of this job (lower number = higher priority).
|
|
38
|
+
* @default 10
|
|
39
|
+
*/
|
|
40
|
+
priority?: number;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Maximum number of retries for this job.
|
|
44
|
+
* Overrides both the job class and queue settings.
|
|
45
|
+
*/
|
|
46
|
+
maxRetries?: number;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Delay between retries in milliseconds.
|
|
50
|
+
* Overrides both the job class and queue settings.
|
|
51
|
+
*/
|
|
52
|
+
retryDelay?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Optional job ID to use for this dispatch.
|
|
56
|
+
* If not provided, a unique ID will be generated.
|
|
57
|
+
*/
|
|
58
|
+
jobId?: string;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A unique ID for this specific dispatch of the job.
|
|
62
|
+
* If not provided, one will be generated.
|
|
63
|
+
*/
|
|
64
|
+
dispatchId?: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Defines how the job handles overlap with other instances of the same job ID.
|
|
68
|
+
* Overrides the setting from the job class.
|
|
69
|
+
*/
|
|
70
|
+
overlapBehavior?: JobOverlapBehavior;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* When using NO_OVERLAP behavior, specifies the delay in milliseconds
|
|
74
|
+
* between sequential executions of jobs with the same ID.
|
|
75
|
+
* Overrides the setting from the job class.
|
|
76
|
+
*/
|
|
77
|
+
overlapDelay?: number;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Configuration options for a Queue.
|
|
82
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
83
|
+
*/
|
|
84
|
+
export type QueueOptions = {
|
|
85
|
+
/**
|
|
86
|
+
* The name of the queue.
|
|
87
|
+
*/
|
|
88
|
+
name: string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The transport class to use for this queue.
|
|
92
|
+
*/
|
|
93
|
+
transport?: TransportClass;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The maximum number of concurrent jobs to process in this queue.
|
|
97
|
+
* @default 1
|
|
98
|
+
*/
|
|
99
|
+
concurrency?: number;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The maximum number of retries for failed jobs.
|
|
103
|
+
* @default 0
|
|
104
|
+
*/
|
|
105
|
+
maxRetries?: number;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The delay in milliseconds between retries.
|
|
109
|
+
* @default 1000
|
|
110
|
+
*/
|
|
111
|
+
retryDelay?: number;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Whether to pause processing when an error occurs.
|
|
115
|
+
* @default false
|
|
116
|
+
*/
|
|
117
|
+
pauseOnError?: boolean;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Configuration options for the transport.
|
|
121
|
+
* This is passed to the transport constructor.
|
|
122
|
+
*/
|
|
123
|
+
transportOptions?: Record<string, any>;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generated ID when dispatching jobs in queues.
|
|
128
|
+
* @author Axel Nana <axel.nana@workbud.com>
|
|
129
|
+
*/
|
|
130
|
+
export type JobDispatchId = {
|
|
131
|
+
/**
|
|
132
|
+
* The job ID.
|
|
133
|
+
*/
|
|
134
|
+
jobId: string;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The dispatch ID.
|
|
138
|
+
*/
|
|
139
|
+
dispatchId: string;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Queue class for dispatching jobs to workers.
|
|
144
|
+
* @author Axel Nana <axel.nana@icloud.com>
|
|
145
|
+
*/
|
|
146
|
+
export class Queue extends InteractsWithConsole {
|
|
147
|
+
/**
|
|
148
|
+
* Map of all registered queues.
|
|
149
|
+
*/
|
|
150
|
+
private static queues: Map<string, Queue> = new Map();
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Checks whether a queue exists.
|
|
154
|
+
* @param name The name of the queue.
|
|
155
|
+
* @returns `true` if the queue exists, `false` otherwise.
|
|
156
|
+
*/
|
|
157
|
+
public static exists(name: string): boolean {
|
|
158
|
+
return this.queues.has(name);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get a queue by name, or create it if it doesn't exist.
|
|
163
|
+
*
|
|
164
|
+
* @param name The name of the queue.
|
|
165
|
+
* @param config Optional configuration for the queue if it needs to be created.
|
|
166
|
+
* @returns The queue instance.
|
|
167
|
+
*/
|
|
168
|
+
public static get(name: string, config?: Omit<QueueOptions, 'name'>): Queue {
|
|
169
|
+
if (!this.queues.has(name)) {
|
|
170
|
+
const queue = new Queue({
|
|
171
|
+
name,
|
|
172
|
+
...config,
|
|
173
|
+
transport: config?.transport ?? ThreadTransport
|
|
174
|
+
});
|
|
175
|
+
this.queues.set(name, queue);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return this.queues.get(name)!;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get all registered queues.
|
|
183
|
+
*
|
|
184
|
+
* @returns A map of all registered queues.
|
|
185
|
+
*/
|
|
186
|
+
public static getAll(): Map<string, Queue> {
|
|
187
|
+
return new Map(this.queues);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The name of this queue
|
|
192
|
+
*/
|
|
193
|
+
public readonly name: string;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* The transport used by this queue
|
|
197
|
+
*/
|
|
198
|
+
private readonly transport: Transport;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The transport class for this queue.
|
|
202
|
+
*/
|
|
203
|
+
private transportClass?: TransportClass;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The transport options for this queue. These are passed to the transport constructor.
|
|
207
|
+
*/
|
|
208
|
+
private transportOptions?: Record<string, any>;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The queue configuration options.
|
|
212
|
+
*/
|
|
213
|
+
private options: QueueOptions;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Array of job IDs currently being processed
|
|
217
|
+
*/
|
|
218
|
+
private jobs: Array<JobDispatchId> = [];
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Creates a new Queue instance.
|
|
222
|
+
*
|
|
223
|
+
* @param config The queue configuration.
|
|
224
|
+
*/
|
|
225
|
+
constructor(config: QueueOptions) {
|
|
226
|
+
super();
|
|
227
|
+
|
|
228
|
+
this.name = config.name;
|
|
229
|
+
this.transportClass = config.transport;
|
|
230
|
+
this.transportOptions = config.transportOptions;
|
|
231
|
+
|
|
232
|
+
// Store queue options for worker configuration
|
|
233
|
+
this.options = {
|
|
234
|
+
concurrency: config.concurrency ?? 1,
|
|
235
|
+
maxRetries: config.maxRetries ?? 0,
|
|
236
|
+
retryDelay: config.retryDelay ?? 1000,
|
|
237
|
+
pauseOnError: config.pauseOnError ?? false,
|
|
238
|
+
...config
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Initialize the transport
|
|
242
|
+
this.transport = new (this.transportClass ?? ThreadTransport)(
|
|
243
|
+
TransportMode.PRODUCER,
|
|
244
|
+
this.transportOptions
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Register message handler for job status updates
|
|
248
|
+
this.transport.onMessage(this.handleTransportMessage.bind(this));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public on<TData, TSource = any>(
|
|
252
|
+
event: TransportEvent['type'],
|
|
253
|
+
handler: EventHandler<TData, TSource> | never
|
|
254
|
+
) {
|
|
255
|
+
Event.on(`elysium:heracles:queue:${this.name}:${event}`, handler);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public once<TData, TSource = any>(
|
|
259
|
+
event: TransportEvent['type'],
|
|
260
|
+
handler: EventHandler<TData, TSource> | never
|
|
261
|
+
) {
|
|
262
|
+
Event.once(`elysium:heracles:queue:${this.name}:${event}`, handler);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private emit<TData, TSource = any>(
|
|
266
|
+
event: TransportEvent['type'],
|
|
267
|
+
data: TData,
|
|
268
|
+
source: TSource | null = null
|
|
269
|
+
): void {
|
|
270
|
+
Event.emit(`elysium:heracles:queue:${this.name}:${event}`, data, source);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Handle messages from the transport
|
|
275
|
+
*
|
|
276
|
+
* @param message The message from the transport
|
|
277
|
+
*/
|
|
278
|
+
private async handleTransportMessage(message: TransportEvent): Promise<void> {
|
|
279
|
+
switch (message.type) {
|
|
280
|
+
case 'job:status':
|
|
281
|
+
case 'job:result':
|
|
282
|
+
// Update job status for both status and result messages
|
|
283
|
+
if (message.queue === this.name) {
|
|
284
|
+
// For result messages, log completion
|
|
285
|
+
if (message.type === 'job:result') {
|
|
286
|
+
this.info(`Job ${message.jobId} completed with status: ${message.status}`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Get the existing status if available
|
|
290
|
+
const existingStatus = await this.getJobStatus({
|
|
291
|
+
jobId: message.jobId,
|
|
292
|
+
dispatchId: message.dispatchId
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Preserve creation time if we already have it
|
|
296
|
+
const createdAt = existingStatus?.createdAt ?? new Date().toISOString();
|
|
297
|
+
|
|
298
|
+
// Update job status
|
|
299
|
+
this.updateJobStatus(message.jobId, message.dispatchId, {
|
|
300
|
+
jobId: message.jobId,
|
|
301
|
+
dispatchId: message.dispatchId,
|
|
302
|
+
queue: message.queue,
|
|
303
|
+
status: message.status,
|
|
304
|
+
error: message.error,
|
|
305
|
+
retries: message.retries ?? existingStatus?.retries ?? 0,
|
|
306
|
+
createdAt: createdAt,
|
|
307
|
+
startedAt: (message as any).startedAt ?? existingStatus?.startedAt,
|
|
308
|
+
completedAt: message.completedAt ?? existingStatus?.completedAt,
|
|
309
|
+
updatedAt: message.updatedAt ?? new Date().toISOString()
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
this.emit(message.type, message, this);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Update the status of a job.
|
|
320
|
+
*
|
|
321
|
+
* @param jobId The ID of the job.
|
|
322
|
+
* @param dispatchID The dispatch ID of the job.
|
|
323
|
+
* @param status The new status information.
|
|
324
|
+
*/
|
|
325
|
+
private updateJobStatus(jobId: string, dispatchId: string, status: JobStatusInfo): void {
|
|
326
|
+
// Perform cleanup for completed jobs
|
|
327
|
+
if (
|
|
328
|
+
status.status === JobStatus.COMPLETED ||
|
|
329
|
+
status.status === JobStatus.FAILED ||
|
|
330
|
+
status.status === JobStatus.CANCELLED
|
|
331
|
+
) {
|
|
332
|
+
// TODO: In the future, this could trigger metrics collection or other cleanup tasks
|
|
333
|
+
this.debug(`Job ${jobId} (${dispatchId}) reached terminal state: ${status.status}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Dispatch a job to this queue
|
|
339
|
+
*
|
|
340
|
+
* @param name The name of the job to dispatch
|
|
341
|
+
* @param args The arguments to pass to the job constructor
|
|
342
|
+
* @param options Optional dispatch options
|
|
343
|
+
* @returns A promise that resolves to the created job instance
|
|
344
|
+
*/
|
|
345
|
+
public async dispatch<T extends Job, TClass extends JobClass<T> = JobClass<T>>(
|
|
346
|
+
job: TClass,
|
|
347
|
+
name: string,
|
|
348
|
+
args: ConstructorParameters<TClass> = [] as any,
|
|
349
|
+
options: JobDispatchOptions = {}
|
|
350
|
+
): Promise<JobDispatchId> {
|
|
351
|
+
// Create initial timestamp
|
|
352
|
+
const now = new Date();
|
|
353
|
+
const createdAt = now.toISOString();
|
|
354
|
+
|
|
355
|
+
// Generate or use provided job ID
|
|
356
|
+
const jobId = options.jobId ?? job.generateJobId(...args);
|
|
357
|
+
|
|
358
|
+
// Generate or use provided dispatch ID
|
|
359
|
+
const dispatchId = options.dispatchId ?? `dispatch_${Date.now()}_${uid(8)}`;
|
|
360
|
+
|
|
361
|
+
// Initialize job status
|
|
362
|
+
const initialStatus: JobStatusInfo = {
|
|
363
|
+
jobId,
|
|
364
|
+
dispatchId,
|
|
365
|
+
queue: this.name,
|
|
366
|
+
status:
|
|
367
|
+
options.scheduledFor && options.scheduledFor > now
|
|
368
|
+
? JobStatus.SCHEDULED_FOR_RETRY
|
|
369
|
+
: JobStatus.PENDING,
|
|
370
|
+
retries: 0,
|
|
371
|
+
createdAt: createdAt,
|
|
372
|
+
updatedAt: createdAt
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
this.updateJobStatus(jobId, dispatchId, initialStatus);
|
|
376
|
+
this.debug(
|
|
377
|
+
`Dispatching job ${jobId} (dispatch: ${dispatchId}, name: ${name}) to queue '${this.name}'`
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Use transport for dispatching
|
|
381
|
+
try {
|
|
382
|
+
const message: TransportEvent = {
|
|
383
|
+
type: 'job:process',
|
|
384
|
+
job: name,
|
|
385
|
+
jobId,
|
|
386
|
+
dispatchId,
|
|
387
|
+
args,
|
|
388
|
+
queue: this.name,
|
|
389
|
+
options: {
|
|
390
|
+
scheduledFor: options.scheduledFor?.toISOString(),
|
|
391
|
+
priority: options.priority,
|
|
392
|
+
maxRetries: options.maxRetries,
|
|
393
|
+
retryDelay: options.retryDelay,
|
|
394
|
+
overlapBehavior: options.overlapBehavior,
|
|
395
|
+
overlapDelay: options.overlapDelay
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
await this.transport.send(message);
|
|
400
|
+
return { jobId: initialStatus.jobId, dispatchId };
|
|
401
|
+
} catch (error: any) {
|
|
402
|
+
this.error(`Failed to dispatch job ${name} to queue '${this.name}': ${error.message}`);
|
|
403
|
+
|
|
404
|
+
// Update job status to failed
|
|
405
|
+
this.updateJobStatus(jobId, dispatchId, {
|
|
406
|
+
jobId: initialStatus.jobId,
|
|
407
|
+
dispatchId,
|
|
408
|
+
queue: this.name,
|
|
409
|
+
status: JobStatus.FAILED,
|
|
410
|
+
error: error.message,
|
|
411
|
+
retries: 0,
|
|
412
|
+
createdAt: initialStatus.createdAt,
|
|
413
|
+
completedAt: new Date().toISOString(),
|
|
414
|
+
updatedAt: new Date().toISOString()
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Cancel a job in this queue.
|
|
423
|
+
*
|
|
424
|
+
* @param id The ID of the dispatched job to cancel.
|
|
425
|
+
* @returns A promise that resolves to true if the job was found and canceled.
|
|
426
|
+
*/
|
|
427
|
+
public async cancelJob(id: JobDispatchId): Promise<boolean> {
|
|
428
|
+
this.debug(`Canceling job ${id.jobId} (${id.dispatchId}) in queue '${this.name}'`);
|
|
429
|
+
|
|
430
|
+
// Use transport interface
|
|
431
|
+
try {
|
|
432
|
+
const message: TransportEvent = {
|
|
433
|
+
type: 'job:cancel',
|
|
434
|
+
jobId: id.jobId,
|
|
435
|
+
dispatchId: id.dispatchId,
|
|
436
|
+
queue: this.name
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
await this.transport.send(message);
|
|
440
|
+
return true;
|
|
441
|
+
} catch (error) {
|
|
442
|
+
this.error(`Failed to cancel job ${id.jobId}: ${error}`);
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Cancel all jobs in this queue.
|
|
449
|
+
*
|
|
450
|
+
* @returns A promise that resolves to the number of jobs canceled.
|
|
451
|
+
*/
|
|
452
|
+
public async cancelAllJobs(): Promise<number> {
|
|
453
|
+
this.debug(`Canceling all jobs in queue '${this.name}'`);
|
|
454
|
+
|
|
455
|
+
try {
|
|
456
|
+
const message: TransportEvent = {
|
|
457
|
+
type: 'job:cancelAll',
|
|
458
|
+
queue: this.name
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
await this.transport.send(message);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
this.error(`Failed to cancel all jobs: ${error}`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// We don't know how many were canceled
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Get the status of a job.
|
|
472
|
+
*
|
|
473
|
+
* @param id The ID of the job.
|
|
474
|
+
* @returns A promise that resolves to the job status or null if not found
|
|
475
|
+
*/
|
|
476
|
+
public async getJobStatus(id: JobDispatchId): Promise<JobStatusInfo | null> {
|
|
477
|
+
// Try to get from transport
|
|
478
|
+
try {
|
|
479
|
+
const status = await this.transport.getJobStatus(id.jobId, id.dispatchId, this.name);
|
|
480
|
+
if (status) {
|
|
481
|
+
this.debug(
|
|
482
|
+
`Retrieved job ${id.jobId} (${id.dispatchId}) status from transport: ${status.status}`
|
|
483
|
+
);
|
|
484
|
+
return status;
|
|
485
|
+
}
|
|
486
|
+
} catch (error) {
|
|
487
|
+
this.warning(`Failed to get job status from transport: ${error}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get all jobs in this queue
|
|
495
|
+
*
|
|
496
|
+
* @returns An array of job IDs
|
|
497
|
+
*/
|
|
498
|
+
public getAllJobs(): Array<JobDispatchId> {
|
|
499
|
+
return Array.from(this.jobs);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Get jobs by status
|
|
504
|
+
*
|
|
505
|
+
* @param status The status to filter by
|
|
506
|
+
* @returns An array of jobs with the specified status
|
|
507
|
+
*/
|
|
508
|
+
public async getJobsByStatus(status: JobStatus): Promise<JobDispatchId[]> {
|
|
509
|
+
const result: JobDispatchId[] = [];
|
|
510
|
+
|
|
511
|
+
for (const [jobId, statusInfo] of await map(
|
|
512
|
+
this.jobs,
|
|
513
|
+
async (id) => [id, await this.getJobStatus(id)] as [JobDispatchId, JobStatusInfo | null]
|
|
514
|
+
)) {
|
|
515
|
+
if (statusInfo?.status === status) {
|
|
516
|
+
result.push(jobId);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Start the queue's transport.
|
|
525
|
+
*
|
|
526
|
+
* @returns A promise that resolves when the transport has started.
|
|
527
|
+
*/
|
|
528
|
+
public async start(): Promise<void> {
|
|
529
|
+
if (this.transport) {
|
|
530
|
+
await this.transport.start();
|
|
531
|
+
this.info(`Queue '${this.name}' transport started`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Stop the queue's transport.
|
|
537
|
+
*
|
|
538
|
+
* @returns A promise that resolves when the transport has stopped.
|
|
539
|
+
*/
|
|
540
|
+
public async stop(): Promise<void> {
|
|
541
|
+
if (this.transport) {
|
|
542
|
+
await this.transport.stop();
|
|
543
|
+
this.info(`Queue '${this.name}' transport stopped`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Get the queue options.
|
|
549
|
+
*
|
|
550
|
+
* @returns The queue options.
|
|
551
|
+
*/
|
|
552
|
+
public getOptions(): QueueOptions {
|
|
553
|
+
return { ...this.options };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Update the queue options.
|
|
558
|
+
*
|
|
559
|
+
* @param options The new options
|
|
560
|
+
*/
|
|
561
|
+
public updateOptions(options: Partial<QueueOptions>): void {
|
|
562
|
+
this.options = {
|
|
563
|
+
...this.options,
|
|
564
|
+
...options
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
}
|