@boringnode/queue 0.3.4 → 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 +79 -38
- package/build/{chunk-H6WOFLPJ.js → chunk-6EBS7CW4.js} +22 -12
- package/build/chunk-6EBS7CW4.js.map +1 -0
- package/build/{chunk-3BIR4IQD.js → chunk-ZZFSQY36.js} +2 -2
- package/build/chunk-ZZFSQY36.js.map +1 -0
- package/build/{index-CoubP-c4.d.ts → index-BAMFA6FI.d.ts} +20 -12
- package/build/index.d.ts +42 -18
- package/build/index.js +101 -8
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +6 -2
- package/build/src/drivers/fake_adapter.js +2 -2
- package/build/src/drivers/knex_adapter.d.ts +5 -1
- package/build/src/drivers/knex_adapter.js +11 -82
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +5 -1
- package/build/src/drivers/redis_adapter.js +42 -21
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +6 -2
- package/build/src/drivers/sync_adapter.js +9 -3
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/package.json +34 -27
- package/build/chunk-3BIR4IQD.js.map +0 -1
- package/build/chunk-H6WOFLPJ.js.map +0 -1
|
@@ -411,7 +411,7 @@ interface JobContext {
|
|
|
411
411
|
* The constructor accepts any arguments for dependency injection.
|
|
412
412
|
* Payload and context are provided separately via `$hydrate()`.
|
|
413
413
|
*/
|
|
414
|
-
type JobClass<T extends Job = Job> = (new (...args:
|
|
414
|
+
type JobClass<T extends Job = Job> = (new (...args: unknown[]) => T) & {
|
|
415
415
|
options?: JobOptions;
|
|
416
416
|
};
|
|
417
417
|
/**
|
|
@@ -452,7 +452,7 @@ interface BackoffConfig {
|
|
|
452
452
|
}
|
|
453
453
|
interface QueueConfig {
|
|
454
454
|
adapter?: string;
|
|
455
|
-
retry?:
|
|
455
|
+
retry?: RetryConfig;
|
|
456
456
|
defaultJobOptions?: JobOptions;
|
|
457
457
|
}
|
|
458
458
|
interface WorkerConfig {
|
|
@@ -506,11 +506,11 @@ interface WorkerConfig {
|
|
|
506
506
|
type WorkerCycle = {
|
|
507
507
|
type: 'started';
|
|
508
508
|
queue: string;
|
|
509
|
-
job:
|
|
509
|
+
job: JobData;
|
|
510
510
|
} | {
|
|
511
511
|
type: 'completed';
|
|
512
512
|
queue: string;
|
|
513
|
-
job:
|
|
513
|
+
job: JobData;
|
|
514
514
|
} | {
|
|
515
515
|
type: 'idle';
|
|
516
516
|
suggestedDelay: Duration;
|
|
@@ -796,6 +796,14 @@ interface Adapter {
|
|
|
796
796
|
* @param config - The schedule configuration
|
|
797
797
|
* @returns The schedule ID
|
|
798
798
|
*/
|
|
799
|
+
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
800
|
+
/**
|
|
801
|
+
* Create or update a schedule.
|
|
802
|
+
*
|
|
803
|
+
* @deprecated Use `upsertSchedule` instead.
|
|
804
|
+
* @param config - The schedule configuration
|
|
805
|
+
* @returns The schedule ID
|
|
806
|
+
*/
|
|
799
807
|
createSchedule(config: ScheduleConfig): Promise<string>;
|
|
800
808
|
/**
|
|
801
809
|
* Get a schedule by ID.
|
|
@@ -987,7 +995,7 @@ declare class JobDispatcher<T> {
|
|
|
987
995
|
* @param onRejected - Error callback
|
|
988
996
|
* @returns Promise resolving to the DispatchResult
|
|
989
997
|
*/
|
|
990
|
-
then(onFulfilled?: (value: DispatchResult) =>
|
|
998
|
+
then<TResult1 = DispatchResult, TResult2 = never>(onFulfilled?: ((value: DispatchResult) => TResult1 | PromiseLike<TResult1>) | null, onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
991
999
|
}
|
|
992
1000
|
|
|
993
1001
|
/**
|
|
@@ -1108,7 +1116,7 @@ declare class JobBatchDispatcher<T> {
|
|
|
1108
1116
|
* @param onRejected - Error callback
|
|
1109
1117
|
* @returns Promise resolving to the DispatchManyResult
|
|
1110
1118
|
*/
|
|
1111
|
-
then(onFulfilled?: (value: DispatchManyResult) =>
|
|
1119
|
+
then<TResult1 = DispatchManyResult, TResult2 = never>(onFulfilled?: ((value: DispatchManyResult) => TResult1 | PromiseLike<TResult1>) | null, onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
1112
1120
|
}
|
|
1113
1121
|
|
|
1114
1122
|
/**
|
|
@@ -1129,9 +1137,9 @@ declare class JobBatchDispatcher<T> {
|
|
|
1129
1137
|
* .run()
|
|
1130
1138
|
* ```
|
|
1131
1139
|
*/
|
|
1132
|
-
declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
1140
|
+
declare class ScheduleBuilder<TPayload = unknown> implements PromiseLike<ScheduleResult> {
|
|
1133
1141
|
#private;
|
|
1134
|
-
constructor(name: string, payload:
|
|
1142
|
+
constructor(name: string, payload: TPayload);
|
|
1135
1143
|
/**
|
|
1136
1144
|
* Set a custom schedule ID.
|
|
1137
1145
|
* If not specified, defaults to the job name.
|
|
@@ -1179,7 +1187,7 @@ declare class ScheduleBuilder implements PromiseLike<ScheduleResult> {
|
|
|
1179
1187
|
/**
|
|
1180
1188
|
* Implement PromiseLike to allow `await builder.every('5m')` syntax.
|
|
1181
1189
|
*/
|
|
1182
|
-
then<TResult1 = ScheduleResult, TResult2 = never>(onfulfilled?: ((value: ScheduleResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason:
|
|
1190
|
+
then<TResult1 = ScheduleResult, TResult2 = never>(onfulfilled?: ((value: ScheduleResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
1183
1191
|
}
|
|
1184
1192
|
|
|
1185
1193
|
/**
|
|
@@ -1331,7 +1339,7 @@ declare abstract class Job<Payload = any> {
|
|
|
1331
1339
|
* .run()
|
|
1332
1340
|
* ```
|
|
1333
1341
|
*/
|
|
1334
|
-
static dispatch<T extends Job>(this: new (...args:
|
|
1342
|
+
static dispatch<T extends Job>(this: new (...args: unknown[]) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
|
|
1335
1343
|
/**
|
|
1336
1344
|
* Dispatch multiple jobs to the queue in a single batch.
|
|
1337
1345
|
*
|
|
@@ -1359,7 +1367,7 @@ declare abstract class Job<Payload = any> {
|
|
|
1359
1367
|
* console.log(`Dispatched ${jobIds.length} jobs`)
|
|
1360
1368
|
* ```
|
|
1361
1369
|
*/
|
|
1362
|
-
static dispatchMany<T extends Job>(this: new (...args:
|
|
1370
|
+
static dispatchMany<T extends Job>(this: new (...args: unknown[]) => T, payloads: (T extends Job<infer P> ? P : never)[]): JobBatchDispatcher<T extends Job<infer P> ? P : never>;
|
|
1363
1371
|
/**
|
|
1364
1372
|
* Create a schedule for this job.
|
|
1365
1373
|
*
|
|
@@ -1385,7 +1393,7 @@ declare abstract class Job<Payload = any> {
|
|
|
1385
1393
|
* .run()
|
|
1386
1394
|
* ```
|
|
1387
1395
|
*/
|
|
1388
|
-
static schedule<T extends Job>(this: new (...args:
|
|
1396
|
+
static schedule<T extends Job>(this: new (...args: unknown[]) => T, payload: T extends Job<infer P> ? P : never): ScheduleBuilder<T extends Job<infer P> ? P : never>;
|
|
1389
1397
|
/**
|
|
1390
1398
|
* Execute the job's business logic.
|
|
1391
1399
|
*
|
package/build/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Q as QueueManagerConfig, W as WorkerCycle, A as Adapter, R as RetryConfig, g as JobFactory, h as JobOptions, i as Job, a as JobClass, e as ScheduleData, j as ScheduleStatus, f as ScheduleListOptions } from './index-
|
|
2
|
-
export { l as JobBatchDispatcher, k as ScheduleBuilder, m as customBackoff, o as exponentialBackoff, p as fixedBackoff, n as linearBackoff } from './index-
|
|
1
|
+
import { Q as QueueManagerConfig, W as WorkerCycle, A as Adapter, R as RetryConfig, g as JobFactory, h as JobOptions, i as Job, a as JobClass, e as ScheduleData, j as ScheduleStatus, f as ScheduleListOptions } from './index-BAMFA6FI.js';
|
|
2
|
+
export { l as JobBatchDispatcher, k as ScheduleBuilder, m as customBackoff, o as exponentialBackoff, p as fixedBackoff, n as linearBackoff } from './index-BAMFA6FI.js';
|
|
3
3
|
import { FakeAdapter } from './src/drivers/fake_adapter.js';
|
|
4
|
-
import
|
|
4
|
+
import { Knex } from 'knex';
|
|
5
|
+
import * as _poppinss_utils_exception from '@poppinss/utils/exception';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Job processing worker.
|
|
@@ -398,7 +399,7 @@ declare class Schedule {
|
|
|
398
399
|
constructor(data: ScheduleData);
|
|
399
400
|
get id(): string;
|
|
400
401
|
get name(): string;
|
|
401
|
-
get payload():
|
|
402
|
+
get payload(): unknown;
|
|
402
403
|
get cronExpression(): string | null;
|
|
403
404
|
get everyMs(): number | null;
|
|
404
405
|
get timezone(): string;
|
|
@@ -447,19 +448,42 @@ declare class Schedule {
|
|
|
447
448
|
trigger(): Promise<void>;
|
|
448
449
|
}
|
|
449
450
|
|
|
450
|
-
declare
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
451
|
+
declare class QueueSchemaService {
|
|
452
|
+
#private;
|
|
453
|
+
constructor(connection: Knex);
|
|
454
|
+
/**
|
|
455
|
+
* Creates the jobs table with the default schema.
|
|
456
|
+
* The optional callback allows adding custom columns.
|
|
457
|
+
*/
|
|
458
|
+
createJobsTable(tableName?: string, extend?: (table: Knex.CreateTableBuilder) => void): Promise<void>;
|
|
459
|
+
/**
|
|
460
|
+
* Creates the schedules table with the default schema.
|
|
461
|
+
* The optional callback allows adding custom columns.
|
|
462
|
+
*/
|
|
463
|
+
createSchedulesTable(tableName?: string, extend?: (table: Knex.CreateTableBuilder) => void): Promise<void>;
|
|
464
|
+
/**
|
|
465
|
+
* Drops the jobs table if it exists.
|
|
466
|
+
*/
|
|
467
|
+
dropJobsTable(tableName?: string): Promise<void>;
|
|
468
|
+
/**
|
|
469
|
+
* Drops the schedules table if it exists.
|
|
470
|
+
*/
|
|
471
|
+
dropSchedulesTable(tableName?: string): Promise<void>;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
declare const E_INVALID_DURATION_EXPRESSION: new (args?: any, options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
475
|
+
declare const E_INVALID_BASE_DELAY: new (args: [reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
476
|
+
declare const E_INVALID_MAX_DELAY: new (args: [reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
477
|
+
declare const E_INVALID_MULTIPLIER: new (args: [reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
478
|
+
declare const E_CONFIGURATION_ERROR: new (args: [reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
479
|
+
declare const E_JOB_NOT_FOUND: new (args: [jobName: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
480
|
+
declare const E_JOB_MAX_ATTEMPTS_REACHED: new (args: [jobName: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
481
|
+
declare const E_JOB_TIMEOUT: new (args: [jobName: string, timeout: number], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
482
|
+
declare const E_QUEUE_NOT_INITIALIZED: new (args?: any, options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
483
|
+
declare const E_ADAPTER_INIT_ERROR: new (args: [adapterName: string, originalMessage: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
484
|
+
declare const E_NO_JOBS_FOUND: new (args: [patterns: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
485
|
+
declare const E_INVALID_CRON_EXPRESSION: new (args: [expression: string, reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
486
|
+
declare const E_INVALID_SCHEDULE_CONFIG: new (args: [reason: string], options?: ErrorOptions) => _poppinss_utils_exception.Exception;
|
|
463
487
|
|
|
464
488
|
declare const exceptions_E_ADAPTER_INIT_ERROR: typeof E_ADAPTER_INIT_ERROR;
|
|
465
489
|
declare const exceptions_E_CONFIGURATION_ERROR: typeof E_CONFIGURATION_ERROR;
|
|
@@ -478,4 +502,4 @@ declare namespace exceptions {
|
|
|
478
502
|
export { exceptions_E_ADAPTER_INIT_ERROR as E_ADAPTER_INIT_ERROR, exceptions_E_CONFIGURATION_ERROR as E_CONFIGURATION_ERROR, exceptions_E_INVALID_BASE_DELAY as E_INVALID_BASE_DELAY, exceptions_E_INVALID_CRON_EXPRESSION as E_INVALID_CRON_EXPRESSION, exceptions_E_INVALID_DURATION_EXPRESSION as E_INVALID_DURATION_EXPRESSION, exceptions_E_INVALID_MAX_DELAY as E_INVALID_MAX_DELAY, exceptions_E_INVALID_MULTIPLIER as E_INVALID_MULTIPLIER, exceptions_E_INVALID_SCHEDULE_CONFIG as E_INVALID_SCHEDULE_CONFIG, exceptions_E_JOB_MAX_ATTEMPTS_REACHED as E_JOB_MAX_ATTEMPTS_REACHED, exceptions_E_JOB_NOT_FOUND as E_JOB_NOT_FOUND, exceptions_E_JOB_TIMEOUT as E_JOB_TIMEOUT, exceptions_E_NO_JOBS_FOUND as E_NO_JOBS_FOUND, exceptions_E_QUEUE_NOT_INITIALIZED as E_QUEUE_NOT_INITIALIZED };
|
|
479
503
|
}
|
|
480
504
|
|
|
481
|
-
export { Job, Locator, QueueManager, Schedule, Worker, exceptions as errors };
|
|
505
|
+
export { Job, Locator, QueueManager, QueueSchemaService, Schedule, Worker, exceptions as errors };
|
package/build/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
QueueManager,
|
|
7
7
|
ScheduleBuilder,
|
|
8
8
|
debug_default
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6EBS7CW4.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_ERROR_RETRY_DELAY,
|
|
12
12
|
DEFAULT_IDLE_DELAY,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
E_JOB_TIMEOUT,
|
|
21
21
|
exceptions_exports,
|
|
22
22
|
parse
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-ZZFSQY36.js";
|
|
24
24
|
|
|
25
25
|
// src/worker.ts
|
|
26
26
|
import { randomUUID } from "crypto";
|
|
@@ -400,18 +400,43 @@ var Worker = class {
|
|
|
400
400
|
return void 0;
|
|
401
401
|
}
|
|
402
402
|
async #executeWithTimeout(instance, payload, context, timeout) {
|
|
403
|
-
if (
|
|
403
|
+
if (timeout === void 0) {
|
|
404
404
|
instance.$hydrate(payload, context);
|
|
405
405
|
return instance.execute();
|
|
406
406
|
}
|
|
407
407
|
const signal = AbortSignal.timeout(timeout);
|
|
408
408
|
instance.$hydrate(payload, context, signal);
|
|
409
|
+
const { abortPromise, cleanupAbortListener } = this.#createTimeoutAbortRace(
|
|
410
|
+
signal,
|
|
411
|
+
instance.constructor.name,
|
|
412
|
+
timeout
|
|
413
|
+
);
|
|
414
|
+
try {
|
|
415
|
+
await Promise.race([instance.execute(), abortPromise]);
|
|
416
|
+
} finally {
|
|
417
|
+
cleanupAbortListener();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
#createTimeoutAbortRace(signal, jobName, timeout) {
|
|
421
|
+
let abortHandler;
|
|
409
422
|
const abortPromise = new Promise((_, reject) => {
|
|
410
|
-
|
|
411
|
-
reject(new E_JOB_TIMEOUT([
|
|
412
|
-
}
|
|
423
|
+
abortHandler = () => {
|
|
424
|
+
reject(new E_JOB_TIMEOUT([jobName, timeout]));
|
|
425
|
+
};
|
|
426
|
+
if (signal.aborted) {
|
|
427
|
+
abortHandler();
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
413
431
|
});
|
|
414
|
-
|
|
432
|
+
return {
|
|
433
|
+
abortPromise,
|
|
434
|
+
cleanupAbortListener: () => {
|
|
435
|
+
if (abortHandler) {
|
|
436
|
+
signal.removeEventListener("abort", abortHandler);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
};
|
|
415
440
|
}
|
|
416
441
|
async #acquireNextJob(queues) {
|
|
417
442
|
for (const queue of queues) {
|
|
@@ -614,8 +639,75 @@ var Schedule = class _Schedule {
|
|
|
614
639
|
}
|
|
615
640
|
};
|
|
616
641
|
|
|
642
|
+
// src/services/queue_schema.ts
|
|
643
|
+
var QueueSchemaService = class {
|
|
644
|
+
#connection;
|
|
645
|
+
constructor(connection) {
|
|
646
|
+
this.#connection = connection;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Creates the jobs table with the default schema.
|
|
650
|
+
* The optional callback allows adding custom columns.
|
|
651
|
+
*/
|
|
652
|
+
async createJobsTable(tableName = "queue_jobs", extend) {
|
|
653
|
+
await this.#connection.schema.createTable(tableName, (table) => {
|
|
654
|
+
table.string("id", 255).notNullable();
|
|
655
|
+
table.string("queue", 255).notNullable();
|
|
656
|
+
table.enu("status", ["pending", "active", "delayed", "completed", "failed"]).notNullable();
|
|
657
|
+
table.text("data").notNullable();
|
|
658
|
+
table.bigint("score").unsigned().nullable();
|
|
659
|
+
table.string("worker_id", 255).nullable();
|
|
660
|
+
table.bigint("acquired_at").unsigned().nullable();
|
|
661
|
+
table.bigint("execute_at").unsigned().nullable();
|
|
662
|
+
table.bigint("finished_at").unsigned().nullable();
|
|
663
|
+
table.text("error").nullable();
|
|
664
|
+
table.primary(["id", "queue"]);
|
|
665
|
+
table.index(["queue", "status", "score"]);
|
|
666
|
+
table.index(["queue", "status", "execute_at"]);
|
|
667
|
+
table.index(["queue", "status", "finished_at"]);
|
|
668
|
+
extend?.(table);
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Creates the schedules table with the default schema.
|
|
673
|
+
* The optional callback allows adding custom columns.
|
|
674
|
+
*/
|
|
675
|
+
async createSchedulesTable(tableName = "queue_schedules", extend) {
|
|
676
|
+
await this.#connection.schema.createTable(tableName, (table) => {
|
|
677
|
+
table.string("id", 255).primary();
|
|
678
|
+
table.string("status", 50).notNullable().defaultTo("active");
|
|
679
|
+
table.string("name", 255).notNullable();
|
|
680
|
+
table.text("payload").notNullable();
|
|
681
|
+
table.string("cron_expression", 255).nullable();
|
|
682
|
+
table.bigint("every_ms").unsigned().nullable();
|
|
683
|
+
table.string("timezone", 100).notNullable().defaultTo("UTC");
|
|
684
|
+
table.timestamp("from_date").nullable();
|
|
685
|
+
table.timestamp("to_date").nullable();
|
|
686
|
+
table.integer("run_limit").unsigned().nullable();
|
|
687
|
+
table.integer("run_count").unsigned().notNullable().defaultTo(0);
|
|
688
|
+
table.timestamp("next_run_at").nullable();
|
|
689
|
+
table.timestamp("last_run_at").nullable();
|
|
690
|
+
table.timestamp("created_at").notNullable().defaultTo(this.#connection.fn.now());
|
|
691
|
+
table.index(["status", "next_run_at"]);
|
|
692
|
+
extend?.(table);
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Drops the jobs table if it exists.
|
|
697
|
+
*/
|
|
698
|
+
async dropJobsTable(tableName = "queue_jobs") {
|
|
699
|
+
await this.#connection.schema.dropTableIfExists(tableName);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Drops the schedules table if it exists.
|
|
703
|
+
*/
|
|
704
|
+
async dropSchedulesTable(tableName = "queue_schedules") {
|
|
705
|
+
await this.#connection.schema.dropTableIfExists(tableName);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
|
|
617
709
|
// src/strategies/backoff_strategy.ts
|
|
618
|
-
import { RuntimeException } from "@poppinss/utils";
|
|
710
|
+
import { RuntimeException } from "@poppinss/utils/exception";
|
|
619
711
|
import { assertUnreachable } from "@poppinss/utils/assert";
|
|
620
712
|
var BackoffStrategy = class {
|
|
621
713
|
#config;
|
|
@@ -764,6 +856,7 @@ export {
|
|
|
764
856
|
JobBatchDispatcher,
|
|
765
857
|
Locator,
|
|
766
858
|
QueueManager,
|
|
859
|
+
QueueSchemaService,
|
|
767
860
|
Schedule,
|
|
768
861
|
ScheduleBuilder,
|
|
769
862
|
Worker,
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts","../src/job_pool.ts","../src/schedule.ts","../src/strategies/backoff_strategy.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { setTimeout } from 'node:timers/promises'\nimport debug from './debug.js'\nimport { parse } from './utils.js'\nimport * as errors from './exceptions.js'\nimport { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport type { Adapter, AcquiredJob } from './contracts/adapter.js'\nimport type { JobContext, JobOptions, QueueManagerConfig, WorkerCycle } from './types/main.js'\nimport { Locator } from './locator.js'\nimport { DEFAULT_PRIORITY } from './constants.js'\nimport type { Job } from './job.js'\nimport {\n DEFAULT_IDLE_DELAY,\n DEFAULT_STALLED_INTERVAL,\n DEFAULT_STALLED_THRESHOLD,\n DEFAULT_ERROR_RETRY_DELAY,\n} from './constants.js'\n\n/**\n * Job processing worker.\n *\n * The Worker continuously polls queues for jobs and executes them\n * with configurable concurrency. It handles:\n * - Concurrent job execution via JobPool\n * - Automatic retries with backoff strategies\n * - Stalled job detection and recovery\n * - Graceful shutdown on SIGINT/SIGTERM\n *\n * @example\n * ```typescript\n * import { Worker, redis } from '@boringnode/queue'\n *\n * const worker = new Worker({\n * default: 'redis',\n * adapters: { redis: redis() },\n * locations: ['./jobs/**\\/*.js'],\n * worker: {\n * concurrency: 5,\n * idleDelay: '1s',\n * },\n * })\n *\n * // Start processing jobs\n * await worker.start(['default', 'emails'])\n *\n * // Or for testing, process one cycle at a time\n * const cycle = await worker.processCycle(['default'])\n * ```\n */\nexport class Worker {\n readonly #id: string\n readonly #config: QueueManagerConfig\n readonly #idleDelay: number\n readonly #stalledInterval: number\n readonly #stalledThreshold: number\n readonly #maxStalledCount: number\n readonly #concurrency: number\n readonly #gracefulShutdown: boolean\n readonly #onShutdownSignal?: () => void | Promise<void>\n\n #adapter!: Adapter\n #running = false\n #initialized = false\n #generator?: AsyncGenerator<WorkerCycle, void, unknown>\n #pool?: JobPool\n #lastStalledCheck = 0\n #shutdownHandler?: () => Promise<void>\n\n /** Unique identifier for this worker instance */\n get id() {\n return this.#id\n }\n\n /**\n * Create a new worker instance.\n *\n * @param config - Queue configuration including adapter and worker settings\n */\n constructor(config: QueueManagerConfig) {\n this.#config = config\n this.#id = randomUUID()\n\n // Parse worker config once at construction\n this.#idleDelay = parse(config.worker?.idleDelay ?? DEFAULT_IDLE_DELAY)\n this.#stalledInterval = parse(config.worker?.stalledInterval ?? DEFAULT_STALLED_INTERVAL)\n this.#stalledThreshold = parse(config.worker?.stalledThreshold ?? DEFAULT_STALLED_THRESHOLD)\n this.#maxStalledCount = config.worker?.maxStalledCount ?? 1\n this.#concurrency = config.worker?.concurrency ?? 1\n this.#gracefulShutdown = config.worker?.gracefulShutdown ?? true\n this.#onShutdownSignal = config.worker?.onShutdownSignal\n\n debug('created worker with id %s and config %O', this.#id, config)\n }\n\n /**\n * Initialize the worker (called automatically by `start()`).\n *\n * Sets up the QueueManager and adapter connection.\n */\n async init() {\n if (this.#initialized) {\n return\n }\n\n debug('initializing worker %s', this.#id)\n\n await QueueManager.init(this.#config)\n\n this.#adapter = QueueManager.use()\n this.#adapter.setWorkerId(this.#id)\n\n this.#initialized = true\n\n debug('worker %s initialized', this.#id)\n }\n\n /**\n * Start processing jobs from the specified queues.\n *\n * This method blocks until the worker is stopped (via `stop()` or signal).\n * Jobs are processed concurrently up to the configured concurrency limit.\n *\n * @param queues - Queue names to process (default: ['default'])\n *\n * @example\n * ```typescript\n * // Process single queue\n * await worker.start()\n *\n * // Process multiple queues (priority order)\n * await worker.start(['high-priority', 'default', 'low-priority'])\n * ```\n */\n async start(queues: string[] = ['default']): Promise<void> {\n await this.init()\n\n if (this.#running) {\n debug('worker %s is already running', this.#id)\n return\n }\n\n this.#running = true\n\n debug('starting worker %s on queues: %O', this.#id, queues)\n\n this.#setupGracefulShutdown()\n\n for await (const cycle of this.process(queues)) {\n if (['started', 'completed'].includes(cycle.type)) {\n continue\n }\n\n if (['idle', 'error'].includes(cycle.type)) {\n // @ts-expect-error - we know suggestedDelay exists for these types\n const delay = parse(cycle.suggestedDelay)\n\n if (cycle.type === 'error') {\n debug('worker %s encountered an error: %O', this.#id, cycle.error)\n } else {\n debug('worker %s is idle, waiting for %dms', this.#id, delay)\n }\n\n await setTimeout(delay)\n }\n }\n }\n\n /**\n * Stop the worker gracefully.\n *\n * Waits for all running jobs to complete before shutting down.\n * Called automatically on SIGINT/SIGTERM if gracefulShutdown is enabled.\n */\n async stop() {\n debug('stopping worker %s', this.#id)\n\n this.#running = false\n\n if (this.#pool) {\n debug('worker %s: waiting for %d running jobs to complete', this.#id, this.#pool.size)\n await this.#pool.drain()\n }\n\n if (this.#adapter) {\n await this.#adapter.destroy()\n }\n\n this.#removeShutdownHandlers()\n }\n\n /**\n * Process a single cycle and return the result.\n *\n * Useful for testing or when you need fine-grained control.\n * Each cycle may start new jobs, complete a job, or return idle.\n *\n * @param queues - Queue names to process\n * @returns The cycle result, or null if the worker was stopped\n *\n * @example\n * ```typescript\n * const worker = new Worker(config)\n *\n * // Process cycles manually\n * let cycle = await worker.processCycle(['default'])\n * while (cycle) {\n * console.log('Cycle:', cycle.type)\n * cycle = await worker.processCycle(['default'])\n * }\n * ```\n */\n async processCycle(queues: string[]): Promise<WorkerCycle | null> {\n await this.init()\n\n this.#running = true\n\n if (!this.#generator) {\n this.#generator = this.process(queues)\n }\n\n const result = await this.#generator.next()\n\n if (result.done) {\n this.#generator = undefined\n return null\n }\n\n return result.value\n }\n\n /**\n * Generator that yields worker cycle events.\n *\n * Low-level API for processing jobs. Yields events for:\n * - `started`: A new job began execution\n * - `completed`: A job finished (success or failure)\n * - `idle`: No jobs available, suggest waiting\n * - `error`: An error occurred during processing\n *\n * @param queues - Queue names to process\n * @yields WorkerCycle events\n *\n * @example\n * ```typescript\n * for await (const cycle of worker.process(['default'])) {\n * switch (cycle.type) {\n * case 'started':\n * console.log(`Started job ${cycle.job.id}`)\n * break\n * case 'completed':\n * console.log(`Completed job ${cycle.job.id}`)\n * break\n * case 'idle':\n * await sleep(cycle.suggestedDelay)\n * break\n * }\n * }\n * ```\n */\n async *process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n this.#pool = new JobPool()\n\n while (this.#running) {\n try {\n // Check for stalled jobs periodically\n await this.#checkStalledJobs(queues)\n\n // Dispatch any due scheduled jobs\n await this.#dispatchDueSchedules()\n\n yield* this.#fillPool(queues)\n\n if (this.#pool.isEmpty()) {\n yield { type: 'idle', suggestedDelay: this.#idleDelay }\n continue\n }\n\n const hasCapacity = this.#pool.hasCapacity(this.#concurrency)\n\n // If we have capacity, don't block indefinitely waiting for a completion;\n // wake up periodically to try to acquire newly enqueued jobs.\n const result = await Promise.race([\n this.#pool\n .waitForNextCompletion()\n .then((completed) => ({ kind: 'completed' as const, completed })),\n ...(hasCapacity\n ? [setTimeout(this.#idleDelay).then(() => ({ kind: 'tick' as const }))]\n : []),\n ])\n\n if (result.kind === 'tick') {\n // No completion yet, but we woke up to check the queue again\n continue\n }\n\n yield { type: 'completed', queue: result.completed.queue, job: result.completed.job }\n } catch (error) {\n yield {\n type: 'error',\n error: error as Error,\n suggestedDelay: parse(DEFAULT_ERROR_RETRY_DELAY),\n }\n }\n }\n }\n\n async *#fillPool(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n const slotsAvailable = this.#concurrency - this.#pool!.size\n\n if (slotsAvailable <= 0) return\n\n const popPromises = Array.from({ length: slotsAvailable }, () => this.#acquireNextJob(queues))\n\n const results = await Promise.all(popPromises)\n\n for (const result of results) {\n if (!result) continue\n\n const { job, queue } = result\n const promise = this.#execute(job, queue)\n this.#pool!.add(job, queue, promise)\n\n yield { type: 'started', queue, job }\n }\n }\n\n async #execute(job: AcquiredJob, queue: string): Promise<void> {\n const startTime = performance.now()\n\n debug('worker %s: executing job %s (%s)', this.#id, job.id, job.name)\n\n const { instance, options, timeout, context, payload } = await this.#initJob(job, queue)\n const retention = QueueManager.getMergedJobOptions(queue, options)\n\n try {\n await this.#executeWithTimeout(instance, payload, context, timeout)\n await this.#adapter.completeJob(job.id, queue, retention.removeOnComplete)\n\n const duration = (performance.now() - startTime).toFixed(2)\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, duration)\n } catch (e) {\n const isTimeout = e instanceof errors.E_JOB_TIMEOUT\n\n if (isTimeout && options.failOnTimeout) {\n debug('worker %s: job %s timed out and failOnTimeout is set', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry)\n\n if (typeof mergedConfig.maxRetries === 'undefined' || mergedConfig.maxRetries <= 0) {\n debug('worker %s: job %s has no retries configured, marking as failed', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n if (job.attempts >= mergedConfig.maxRetries!) {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n mergedConfig.maxRetries\n )\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n const exception = new errors.E_JOB_MAX_ATTEMPTS_REACHED([job.name], { cause: e })\n await instance.failed?.(exception)\n\n return\n }\n\n if (mergedConfig.backoff) {\n const strategy = mergedConfig.backoff()\n const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1)\n\n debug('worker %s: job %s will retry at %s', this.#id, job.id, nextRetryAt.toISOString())\n\n await this.#adapter.retryJob(job.id, queue, nextRetryAt)\n return\n }\n\n await this.#adapter.retryJob(job.id, queue)\n }\n }\n\n async #initJob(\n job: AcquiredJob,\n queue: string\n ): Promise<{\n instance: Job\n options: JobOptions\n timeout: number | undefined\n context: JobContext\n payload: any\n }> {\n try {\n const JobClass = Locator.getOrThrow(job.name)\n\n const context: JobContext = {\n jobId: job.id,\n name: job.name,\n attempt: job.attempts + 1,\n queue,\n priority: job.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(job.acquiredAt),\n stalledCount: job.stalledCount ?? 0,\n }\n\n const jobFactory = QueueManager.getJobFactory()\n const instance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n const options = JobClass.options || {}\n const timeout = this.#getJobTimeout(options)\n\n return { instance, options, timeout, context, payload: job.payload }\n } catch (error) {\n debug('worker %s: failed to initialize job %s (%s)', this.#id, job.id, job.name)\n const retention = QueueManager.getMergedJobOptions(queue)\n await this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail)\n throw error\n }\n }\n\n #getJobTimeout(options: JobOptions): number | undefined {\n if (options.timeout !== undefined) {\n return parse(options.timeout)\n }\n\n if (this.#config.worker?.timeout !== undefined) {\n return parse(this.#config.worker.timeout)\n }\n\n return undefined\n }\n\n async #executeWithTimeout(\n instance: Job,\n payload: any,\n context: JobContext,\n timeout?: number\n ): Promise<void> {\n if (!timeout) {\n instance.$hydrate(payload, context)\n return instance.execute()\n }\n\n const signal = AbortSignal.timeout(timeout)\n instance.$hydrate(payload, context, signal)\n\n const abortPromise = new Promise<never>((_, reject) => {\n signal.addEventListener('abort', () => {\n reject(new errors.E_JOB_TIMEOUT([instance.constructor.name, timeout]))\n })\n })\n\n await Promise.race([instance.execute(), abortPromise])\n }\n\n async #acquireNextJob(queues: string[]): Promise<{ job: AcquiredJob; queue: string } | null> {\n for (const queue of queues) {\n const job = await this.#adapter.popFrom(queue)\n\n if (!job) {\n continue\n }\n\n debug('worker %s: acquired job %s', this.#id, job.id)\n return { job, queue }\n }\n\n return null\n }\n\n async #checkStalledJobs(queues: string[]): Promise<void> {\n const now = Date.now()\n\n // Only check if enough time has passed since last check\n if (now - this.#lastStalledCheck < this.#stalledInterval) {\n return\n }\n\n this.#lastStalledCheck = now\n\n for (const queue of queues) {\n const recovered = await this.#adapter.recoverStalledJobs(\n queue,\n this.#stalledThreshold,\n this.#maxStalledCount\n )\n\n if (recovered > 0) {\n debug('worker %s: recovered %d stalled jobs from queue %s', this.#id, recovered, queue)\n }\n }\n }\n\n #setupGracefulShutdown() {\n if (!this.#gracefulShutdown) {\n return\n }\n\n this.#shutdownHandler = async () => {\n debug('received shutdown signal, stopping worker...')\n\n if (this.#onShutdownSignal) {\n await this.#onShutdownSignal()\n }\n\n await this.stop()\n }\n\n process.on('SIGINT', this.#shutdownHandler)\n process.on('SIGTERM', this.#shutdownHandler)\n }\n\n #removeShutdownHandlers() {\n if (this.#shutdownHandler) {\n process.off('SIGINT', this.#shutdownHandler)\n process.off('SIGTERM', this.#shutdownHandler)\n this.#shutdownHandler = undefined\n }\n }\n\n /**\n * Dispatch any due scheduled jobs.\n *\n * Claims due schedules from the adapter and dispatches the corresponding\n * jobs to their configured queues.\n */\n async #dispatchDueSchedules(): Promise<void> {\n // Keep claiming due schedules until there are none left\n while (true) {\n const schedule = await this.#adapter.claimDueSchedule()\n\n if (!schedule) {\n break\n }\n\n debug(\n 'worker %s: dispatching scheduled job %s (schedule: %s, runCount: %d)',\n this.#id,\n schedule.name,\n schedule.id,\n schedule.runCount + 1\n )\n\n // Get the job class to determine the target queue\n const JobClass = Locator.get(schedule.name)\n const queue = JobClass?.options?.queue ?? 'default'\n\n // Dispatch the job to the queue\n await this.#adapter.pushOn(queue, {\n id: randomUUID(),\n name: schedule.name,\n payload: schedule.payload,\n attempts: 0,\n priority: JobClass?.options?.priority,\n })\n }\n }\n}\n","import type { AcquiredJob } from './contracts/adapter.js'\n\n/**\n * Entry representing an active job in the pool.\n */\ninterface PoolEntry {\n /** Promise that resolves when the job completes */\n promise: Promise<void>\n /** The acquired job data */\n job: AcquiredJob\n /** The queue this job came from */\n queue: string\n}\n\n/**\n * Manages concurrent job execution with a fixed pool size.\n *\n * The pool tracks running jobs and returns the first one to complete,\n * allowing maximum throughput regardless of individual job duration:\n *\n * ```\n * Job A: ████████████████████░░░░░░░░░░ (slow - 10s)\n * Job B: ████ done (fast - 100ms) ← returns first\n * Job C: ████████████░░░░░░░░░░░░░░░░░░ (medium - 2s)\n * ↑\n * Slot freed, new job can start immediately\n * ```\n *\n * Key insight: slow jobs don't block the pool. As soon as any job\n * completes, its slot becomes available for new work.\n */\nexport class JobPool {\n #activeJobs = new Map<string, PoolEntry>()\n\n /** Number of currently running jobs */\n get size() {\n return this.#activeJobs.size\n }\n\n /**\n * Check if the pool has no running jobs.\n *\n * @returns True if no jobs are running\n */\n isEmpty() {\n return this.#activeJobs.size === 0\n }\n\n /**\n * Check if the pool can accept more jobs.\n *\n * @param concurrency - Maximum number of concurrent jobs\n * @returns True if there's room for more jobs\n */\n hasCapacity(concurrency: number) {\n return this.#activeJobs.size < concurrency\n }\n\n /**\n * Add a job to the pool.\n *\n * @param job - The acquired job data\n * @param queue - The queue the job came from\n * @param promise - Promise that resolves when the job completes\n */\n add(job: AcquiredJob, queue: string, promise: Promise<void>) {\n this.#activeJobs.set(job.id, { promise, job, queue })\n }\n\n /**\n * Wait for the next job to complete and return it.\n *\n * Uses `Promise.race()` internally, so the fastest job wins.\n * The completed job is removed from the pool.\n *\n * @returns The first job to complete (success or failure)\n */\n async waitForNextCompletion(): Promise<PoolEntry> {\n const completedJobId = await Promise.race(\n [...this.#activeJobs.entries()].map(async ([id, { promise }]) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n return id\n })\n )\n\n const completed = this.#activeJobs.get(completedJobId)!\n this.#activeJobs.delete(completedJobId)\n\n return completed\n }\n\n /**\n * Wait for all running jobs to complete.\n *\n * Used during graceful shutdown to ensure no jobs are abandoned.\n * Clears the pool after all jobs finish.\n */\n async drain(): Promise<void> {\n const promises = [...this.#activeJobs.values()].map(async ({ promise }) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n })\n\n await Promise.all(promises)\n this.#activeJobs.clear()\n }\n}\n","import { QueueManager } from './queue_manager.js'\nimport { JobDispatcher } from './job_dispatcher.js'\nimport type { ScheduleData, ScheduleListOptions, ScheduleStatus } from './types/main.js'\n\n/**\n * Represents a persisted job schedule.\n *\n * Use `Schedule.find()` or `Schedule.list()` to retrieve schedules,\n * then use instance methods to manage them.\n *\n * @example\n * ```typescript\n * const schedule = await Schedule.find('cleanup-daily')\n * if (schedule) {\n * await schedule.pause()\n * // Later...\n * await schedule.resume()\n * }\n *\n * // List all active schedules\n * const activeSchedules = await Schedule.list({ status: 'active' })\n * ```\n */\nexport class Schedule {\n readonly #data: ScheduleData\n\n constructor(data: ScheduleData) {\n this.#data = data\n }\n\n get id(): string {\n return this.#data.id\n }\n\n get name(): string {\n return this.#data.name\n }\n\n get payload(): any {\n return this.#data.payload\n }\n\n get cronExpression(): string | null {\n return this.#data.cronExpression\n }\n\n get everyMs(): number | null {\n return this.#data.everyMs\n }\n\n get timezone(): string {\n return this.#data.timezone\n }\n\n get from(): Date | null {\n return this.#data.from\n }\n\n get to(): Date | null {\n return this.#data.to\n }\n\n get limit(): number | null {\n return this.#data.limit\n }\n\n get runCount(): number {\n return this.#data.runCount\n }\n\n get nextRunAt(): Date | null {\n return this.#data.nextRunAt\n }\n\n get lastRunAt(): Date | null {\n return this.#data.lastRunAt\n }\n\n get status(): ScheduleStatus {\n return this.#data.status\n }\n\n get createdAt(): Date {\n return this.#data.createdAt\n }\n\n /**\n * Find a schedule by ID.\n *\n * @param id - The schedule ID\n * @returns The schedule instance, or null if not found\n */\n static async find(id: string): Promise<Schedule | null> {\n const adapter = QueueManager.use()\n const data = await adapter.getSchedule(id)\n\n if (!data) return null\n\n return new Schedule(data)\n }\n\n /**\n * List all schedules matching the given options.\n *\n * @param options - Optional filters for listing\n * @returns Array of schedule instances\n */\n static async list(options?: ScheduleListOptions): Promise<Schedule[]> {\n const adapter = QueueManager.use()\n const schedules = await adapter.listSchedules(options)\n\n return schedules.map((data) => new Schedule(data))\n }\n\n /**\n * Pause this schedule.\n * No jobs will be dispatched while paused.\n */\n async pause(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'paused' })\n this.#data.status = 'paused'\n }\n\n /**\n * Resume this schedule.\n * Jobs will be dispatched according to the schedule.\n */\n async resume(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'active' })\n this.#data.status = 'active'\n }\n\n /**\n * Delete this schedule permanently.\n */\n async delete(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.deleteSchedule(this.#data.id)\n }\n\n /**\n * Trigger immediate execution of this schedule's job.\n * Also updates runCount and lastRunAt.\n *\n * If the schedule has reached its limit, the job will not be dispatched.\n */\n async trigger(): Promise<void> {\n // Check if limit is reached\n if (this.#data.limit !== null && this.#data.runCount >= this.#data.limit) {\n return\n }\n\n const adapter = QueueManager.use()\n\n // Dispatch the job\n const dispatcher = new JobDispatcher(this.#data.name, this.#data.payload)\n await dispatcher.run()\n\n // Update run metadata\n const now = new Date()\n const newRunCount = this.#data.runCount + 1\n\n await adapter.updateSchedule(this.#data.id, {\n runCount: newRunCount,\n lastRunAt: now,\n })\n\n this.#data.runCount = newRunCount\n this.#data.lastRunAt = now\n }\n}\n","import type { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils'\nimport { assertUnreachable } from '@poppinss/utils/assert'\n\n/**\n * Calculates retry delays using configurable strategies.\n *\n * Supports three built-in strategies:\n * - `exponential`: Delay doubles each attempt (1s, 2s, 4s, 8s, ...)\n * - `linear`: Delay increases linearly (5s, 10s, 15s, 20s, ...)\n * - `fixed`: Same delay every time (10s, 10s, 10s, ...)\n *\n * All strategies support:\n * - `maxDelay`: Cap the maximum delay\n * - `jitter`: Add randomness to prevent thundering herd\n *\n * @example\n * ```typescript\n * const strategy = new BackoffStrategy({\n * strategy: 'exponential',\n * baseDelay: '1s',\n * maxDelay: '5m',\n * multiplier: 2,\n * jitter: true,\n * })\n *\n * strategy.calculateDelay(1) // ~1000ms\n * strategy.calculateDelay(2) // ~2000ms\n * strategy.calculateDelay(3) // ~4000ms\n * ```\n */\nexport class BackoffStrategy {\n readonly #config: BackoffConfig\n\n /**\n * Create a new backoff strategy.\n *\n * @param config - Backoff configuration\n * @throws {E_INVALID_BASE_DELAY} If baseDelay is not positive\n * @throws {E_INVALID_MAX_DELAY} If maxDelay is invalid\n * @throws {E_INVALID_MULTIPLIER} If multiplier is invalid\n */\n constructor(config: BackoffConfig) {\n this.#config = config\n this.#validateConfig()\n }\n\n /**\n * Calculate the delay for a given attempt number.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Delay in milliseconds\n * @throws {RuntimeException} If attempt is less than 1\n *\n * @example\n * ```typescript\n * // Exponential: 1s, 2s, 4s, 8s, 16s, ...\n * strategy.calculateDelay(1) // 1000\n * strategy.calculateDelay(2) // 2000\n * strategy.calculateDelay(3) // 4000\n * ```\n */\n calculateDelay(attempt: number): number {\n if (attempt < 1) {\n throw new RuntimeException('Attempt number must be >= 1')\n }\n\n const baseDelayMs = parse(this.#config.baseDelay)\n const maxDelayMs = this.#config.maxDelay ? parse(this.#config.maxDelay) : Infinity\n const multiplier = this.#config.multiplier ?? 2\n\n let delay: number\n\n switch (this.#config.strategy) {\n case 'exponential':\n delay = baseDelayMs * Math.pow(multiplier, attempt - 1)\n break\n case 'linear':\n delay = baseDelayMs * attempt\n break\n case 'fixed':\n delay = baseDelayMs\n break\n default:\n assertUnreachable(this.#config.strategy)\n }\n\n // Apply max delay limit\n delay = Math.min(delay, maxDelayMs)\n\n if (this.#config.jitter) {\n delay = this.#applyJitter(delay)\n }\n\n return Math.floor(delay)\n }\n\n /**\n * Get the Date when the next retry should occur.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Date for the next retry\n *\n * @example\n * ```typescript\n * const nextRetry = strategy.getNextRetryAt(3)\n * console.log(`Retry at: ${nextRetry.toISOString()}`)\n * ```\n */\n getNextRetryAt(attempt: number): Date {\n const delay = this.calculateDelay(attempt)\n return new Date(Date.now() + delay)\n }\n\n /**\n * Get a frozen copy of the configuration.\n *\n * @returns Readonly configuration object\n */\n getConfig(): Readonly<BackoffConfig> {\n return Object.freeze({ ...this.#config })\n }\n\n #validateConfig() {\n const baseDelayMs = parse(this.#config.baseDelay)\n\n if (baseDelayMs <= 0) {\n throw new errors.E_INVALID_BASE_DELAY([\n 'Base delay must be a positive integer greater than zero',\n ])\n }\n\n if (this.#config.maxDelay) {\n const maxDelayMs = parse(this.#config.maxDelay)\n\n if (maxDelayMs <= 0) {\n throw new errors.E_INVALID_MAX_DELAY([\n 'Max delay must be a positive integer greater than zero',\n ])\n }\n\n if (maxDelayMs <= baseDelayMs) {\n throw new errors.E_INVALID_MAX_DELAY(['Max delay should be greater than base delay'])\n }\n }\n\n if (this.#config.multiplier !== undefined) {\n if (this.#config.multiplier <= 0) {\n throw new errors.E_INVALID_MULTIPLIER([\n 'Multiplier must be a positive number greater than zero',\n ])\n }\n\n if (this.#config.strategy === 'exponential' && this.#config.multiplier < 1) {\n throw new errors.E_INVALID_MULTIPLIER(['Exponential strategy multiplier should be >= 1'])\n }\n }\n }\n\n #applyJitter(delay: number): number {\n const jitterRange = delay * 0.25\n const jitter = (Math.random() - 0.5) * 2 * jitterRange\n\n return Math.max(0, delay + jitter)\n }\n}\n\n/**\n * Create an exponential backoff strategy factory.\n *\n * Delay doubles with each attempt: 1s → 2s → 4s → 8s → ...\n *\n * Default config:\n * - baseDelay: 1s\n * - maxDelay: 5m\n * - multiplier: 2\n * - jitter: true\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: exponentialBackoff({ baseDelay: '500ms', maxDelay: '1m' }),\n * },\n * }\n * ```\n */\nexport function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'exponential',\n baseDelay: '1s',\n maxDelay: '5m',\n multiplier: 2,\n jitter: true,\n ...config,\n })\n}\n\n/**\n * Create a linear backoff strategy factory.\n *\n * Delay increases linearly: 5s → 10s → 15s → 20s → ...\n *\n * Default config:\n * - baseDelay: 5s\n * - maxDelay: 2m\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: linearBackoff({ baseDelay: '10s' }),\n * },\n * }\n * ```\n */\nexport function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'linear',\n baseDelay: '5s',\n maxDelay: '2m',\n ...config,\n })\n}\n\n/**\n * Create a fixed delay backoff strategy factory.\n *\n * Same delay every time: 10s → 10s → 10s → ...\n *\n * @param delay - The fixed delay (default: '10s')\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: fixedBackoff('30s'),\n * },\n * }\n * ```\n */\nexport function fixedBackoff(delay: Duration = '10s') {\n return () =>\n new BackoffStrategy({\n strategy: 'fixed',\n baseDelay: delay,\n })\n}\n\n/**\n * Create a custom backoff strategy factory.\n *\n * Use this when you need full control over the configuration.\n *\n * @param config - Complete backoff configuration\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: customBackoff({\n * strategy: 'exponential',\n * baseDelay: '100ms',\n * maxDelay: '30s',\n * multiplier: 3,\n * jitter: false,\n * }),\n * },\n * }\n * ```\n */\nexport function customBackoff(config: BackoffConfig) {\n return () => new BackoffStrategy(config)\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts","../src/job_pool.ts","../src/schedule.ts","../src/services/queue_schema.ts","../src/strategies/backoff_strategy.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { setTimeout } from 'node:timers/promises'\nimport debug from './debug.js'\nimport { parse } from './utils.js'\nimport * as errors from './exceptions.js'\nimport { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport type { Adapter, AcquiredJob } from './contracts/adapter.js'\nimport type { JobContext, JobOptions, QueueManagerConfig, WorkerCycle } from './types/main.js'\nimport { Locator } from './locator.js'\nimport { DEFAULT_PRIORITY } from './constants.js'\nimport type { Job } from './job.js'\nimport {\n DEFAULT_IDLE_DELAY,\n DEFAULT_STALLED_INTERVAL,\n DEFAULT_STALLED_THRESHOLD,\n DEFAULT_ERROR_RETRY_DELAY,\n} from './constants.js'\n\n/**\n * Job processing worker.\n *\n * The Worker continuously polls queues for jobs and executes them\n * with configurable concurrency. It handles:\n * - Concurrent job execution via JobPool\n * - Automatic retries with backoff strategies\n * - Stalled job detection and recovery\n * - Graceful shutdown on SIGINT/SIGTERM\n *\n * @example\n * ```typescript\n * import { Worker, redis } from '@boringnode/queue'\n *\n * const worker = new Worker({\n * default: 'redis',\n * adapters: { redis: redis() },\n * locations: ['./jobs/**\\/*.js'],\n * worker: {\n * concurrency: 5,\n * idleDelay: '1s',\n * },\n * })\n *\n * // Start processing jobs\n * await worker.start(['default', 'emails'])\n *\n * // Or for testing, process one cycle at a time\n * const cycle = await worker.processCycle(['default'])\n * ```\n */\nexport class Worker {\n readonly #id: string\n readonly #config: QueueManagerConfig\n readonly #idleDelay: number\n readonly #stalledInterval: number\n readonly #stalledThreshold: number\n readonly #maxStalledCount: number\n readonly #concurrency: number\n readonly #gracefulShutdown: boolean\n readonly #onShutdownSignal?: () => void | Promise<void>\n\n #adapter!: Adapter\n #running = false\n #initialized = false\n #generator?: AsyncGenerator<WorkerCycle, void, unknown>\n #pool?: JobPool\n #lastStalledCheck = 0\n #shutdownHandler?: () => Promise<void>\n\n /** Unique identifier for this worker instance */\n get id() {\n return this.#id\n }\n\n /**\n * Create a new worker instance.\n *\n * @param config - Queue configuration including adapter and worker settings\n */\n constructor(config: QueueManagerConfig) {\n this.#config = config\n this.#id = randomUUID()\n\n // Parse worker config once at construction\n this.#idleDelay = parse(config.worker?.idleDelay ?? DEFAULT_IDLE_DELAY)\n this.#stalledInterval = parse(config.worker?.stalledInterval ?? DEFAULT_STALLED_INTERVAL)\n this.#stalledThreshold = parse(config.worker?.stalledThreshold ?? DEFAULT_STALLED_THRESHOLD)\n this.#maxStalledCount = config.worker?.maxStalledCount ?? 1\n this.#concurrency = config.worker?.concurrency ?? 1\n this.#gracefulShutdown = config.worker?.gracefulShutdown ?? true\n this.#onShutdownSignal = config.worker?.onShutdownSignal\n\n debug('created worker with id %s and config %O', this.#id, config)\n }\n\n /**\n * Initialize the worker (called automatically by `start()`).\n *\n * Sets up the QueueManager and adapter connection.\n */\n async init() {\n if (this.#initialized) {\n return\n }\n\n debug('initializing worker %s', this.#id)\n\n await QueueManager.init(this.#config)\n\n this.#adapter = QueueManager.use()\n this.#adapter.setWorkerId(this.#id)\n\n this.#initialized = true\n\n debug('worker %s initialized', this.#id)\n }\n\n /**\n * Start processing jobs from the specified queues.\n *\n * This method blocks until the worker is stopped (via `stop()` or signal).\n * Jobs are processed concurrently up to the configured concurrency limit.\n *\n * @param queues - Queue names to process (default: ['default'])\n *\n * @example\n * ```typescript\n * // Process single queue\n * await worker.start()\n *\n * // Process multiple queues (priority order)\n * await worker.start(['high-priority', 'default', 'low-priority'])\n * ```\n */\n async start(queues: string[] = ['default']): Promise<void> {\n await this.init()\n\n if (this.#running) {\n debug('worker %s is already running', this.#id)\n return\n }\n\n this.#running = true\n\n debug('starting worker %s on queues: %O', this.#id, queues)\n\n this.#setupGracefulShutdown()\n\n for await (const cycle of this.process(queues)) {\n if (['started', 'completed'].includes(cycle.type)) {\n continue\n }\n\n if (['idle', 'error'].includes(cycle.type)) {\n // @ts-expect-error - we know suggestedDelay exists for these types\n const delay = parse(cycle.suggestedDelay)\n\n if (cycle.type === 'error') {\n debug('worker %s encountered an error: %O', this.#id, cycle.error)\n } else {\n debug('worker %s is idle, waiting for %dms', this.#id, delay)\n }\n\n await setTimeout(delay)\n }\n }\n }\n\n /**\n * Stop the worker gracefully.\n *\n * Waits for all running jobs to complete before shutting down.\n * Called automatically on SIGINT/SIGTERM if gracefulShutdown is enabled.\n */\n async stop() {\n debug('stopping worker %s', this.#id)\n\n this.#running = false\n\n if (this.#pool) {\n debug('worker %s: waiting for %d running jobs to complete', this.#id, this.#pool.size)\n await this.#pool.drain()\n }\n\n if (this.#adapter) {\n await this.#adapter.destroy()\n }\n\n this.#removeShutdownHandlers()\n }\n\n /**\n * Process a single cycle and return the result.\n *\n * Useful for testing or when you need fine-grained control.\n * Each cycle may start new jobs, complete a job, or return idle.\n *\n * @param queues - Queue names to process\n * @returns The cycle result, or null if the worker was stopped\n *\n * @example\n * ```typescript\n * const worker = new Worker(config)\n *\n * // Process cycles manually\n * let cycle = await worker.processCycle(['default'])\n * while (cycle) {\n * console.log('Cycle:', cycle.type)\n * cycle = await worker.processCycle(['default'])\n * }\n * ```\n */\n async processCycle(queues: string[]): Promise<WorkerCycle | null> {\n await this.init()\n\n this.#running = true\n\n if (!this.#generator) {\n this.#generator = this.process(queues)\n }\n\n const result = await this.#generator.next()\n\n if (result.done) {\n this.#generator = undefined\n return null\n }\n\n return result.value\n }\n\n /**\n * Generator that yields worker cycle events.\n *\n * Low-level API for processing jobs. Yields events for:\n * - `started`: A new job began execution\n * - `completed`: A job finished (success or failure)\n * - `idle`: No jobs available, suggest waiting\n * - `error`: An error occurred during processing\n *\n * @param queues - Queue names to process\n * @yields WorkerCycle events\n *\n * @example\n * ```typescript\n * for await (const cycle of worker.process(['default'])) {\n * switch (cycle.type) {\n * case 'started':\n * console.log(`Started job ${cycle.job.id}`)\n * break\n * case 'completed':\n * console.log(`Completed job ${cycle.job.id}`)\n * break\n * case 'idle':\n * await sleep(cycle.suggestedDelay)\n * break\n * }\n * }\n * ```\n */\n async *process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n this.#pool = new JobPool()\n\n while (this.#running) {\n try {\n // Check for stalled jobs periodically\n await this.#checkStalledJobs(queues)\n\n // Dispatch any due scheduled jobs\n await this.#dispatchDueSchedules()\n\n yield* this.#fillPool(queues)\n\n if (this.#pool.isEmpty()) {\n yield { type: 'idle', suggestedDelay: this.#idleDelay }\n continue\n }\n\n const hasCapacity = this.#pool.hasCapacity(this.#concurrency)\n\n // If we have capacity, don't block indefinitely waiting for a completion;\n // wake up periodically to try to acquire newly enqueued jobs.\n const result = await Promise.race([\n this.#pool\n .waitForNextCompletion()\n .then((completed) => ({ kind: 'completed' as const, completed })),\n ...(hasCapacity\n ? [setTimeout(this.#idleDelay).then(() => ({ kind: 'tick' as const }))]\n : []),\n ])\n\n if (result.kind === 'tick') {\n // No completion yet, but we woke up to check the queue again\n continue\n }\n\n yield { type: 'completed', queue: result.completed.queue, job: result.completed.job }\n } catch (error) {\n yield {\n type: 'error',\n error: error as Error,\n suggestedDelay: parse(DEFAULT_ERROR_RETRY_DELAY),\n }\n }\n }\n }\n\n async *#fillPool(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n const slotsAvailable = this.#concurrency - this.#pool!.size\n\n if (slotsAvailable <= 0) return\n\n const popPromises = Array.from({ length: slotsAvailable }, () => this.#acquireNextJob(queues))\n\n const results = await Promise.all(popPromises)\n\n for (const result of results) {\n if (!result) continue\n\n const { job, queue } = result\n const promise = this.#execute(job, queue)\n this.#pool!.add(job, queue, promise)\n\n yield { type: 'started', queue, job }\n }\n }\n\n async #execute(job: AcquiredJob, queue: string): Promise<void> {\n const startTime = performance.now()\n\n debug('worker %s: executing job %s (%s)', this.#id, job.id, job.name)\n\n const { instance, options, timeout, context, payload } = await this.#initJob(job, queue)\n const retention = QueueManager.getMergedJobOptions(queue, options)\n\n try {\n await this.#executeWithTimeout(instance, payload, context, timeout)\n await this.#adapter.completeJob(job.id, queue, retention.removeOnComplete)\n\n const duration = (performance.now() - startTime).toFixed(2)\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, duration)\n } catch (e) {\n const isTimeout = e instanceof errors.E_JOB_TIMEOUT\n\n if (isTimeout && options.failOnTimeout) {\n debug('worker %s: job %s timed out and failOnTimeout is set', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry)\n\n if (typeof mergedConfig.maxRetries === 'undefined' || mergedConfig.maxRetries <= 0) {\n debug('worker %s: job %s has no retries configured, marking as failed', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n await instance.failed?.(e as Error)\n return\n }\n\n if (job.attempts >= mergedConfig.maxRetries!) {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n mergedConfig.maxRetries\n )\n await this.#adapter.failJob(job.id, queue, e as Error, retention.removeOnFail)\n const exception = new errors.E_JOB_MAX_ATTEMPTS_REACHED([job.name], { cause: e })\n await instance.failed?.(exception)\n\n return\n }\n\n if (mergedConfig.backoff) {\n const strategy = mergedConfig.backoff()\n const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1)\n\n debug('worker %s: job %s will retry at %s', this.#id, job.id, nextRetryAt.toISOString())\n\n await this.#adapter.retryJob(job.id, queue, nextRetryAt)\n return\n }\n\n await this.#adapter.retryJob(job.id, queue)\n }\n }\n\n async #initJob(\n job: AcquiredJob,\n queue: string\n ): Promise<{\n instance: Job\n options: JobOptions\n timeout: number | undefined\n context: JobContext\n payload: unknown\n }> {\n try {\n const JobClass = Locator.getOrThrow(job.name)\n\n const context: JobContext = {\n jobId: job.id,\n name: job.name,\n attempt: job.attempts + 1,\n queue,\n priority: job.priority ?? DEFAULT_PRIORITY,\n acquiredAt: new Date(job.acquiredAt),\n stalledCount: job.stalledCount ?? 0,\n }\n\n const jobFactory = QueueManager.getJobFactory()\n const instance = jobFactory ? await jobFactory(JobClass) : new JobClass()\n const options = JobClass.options || {}\n const timeout = this.#getJobTimeout(options)\n\n return { instance, options, timeout, context, payload: job.payload }\n } catch (error) {\n debug('worker %s: failed to initialize job %s (%s)', this.#id, job.id, job.name)\n const retention = QueueManager.getMergedJobOptions(queue)\n await this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail)\n throw error\n }\n }\n\n #getJobTimeout(options: JobOptions): number | undefined {\n if (options.timeout !== undefined) {\n return parse(options.timeout)\n }\n\n if (this.#config.worker?.timeout !== undefined) {\n return parse(this.#config.worker.timeout)\n }\n\n return undefined\n }\n\n async #executeWithTimeout(\n instance: Job,\n payload: unknown,\n context: JobContext,\n timeout?: number\n ): Promise<void> {\n if (timeout === undefined) {\n instance.$hydrate(payload, context)\n return instance.execute()\n }\n\n const signal = AbortSignal.timeout(timeout)\n instance.$hydrate(payload, context, signal)\n\n const { abortPromise, cleanupAbortListener } = this.#createTimeoutAbortRace(\n signal,\n instance.constructor.name,\n timeout\n )\n\n try {\n await Promise.race([instance.execute(), abortPromise])\n } finally {\n cleanupAbortListener()\n }\n }\n\n #createTimeoutAbortRace(signal: AbortSignal, jobName: string, timeout: number) {\n let abortHandler: (() => void) | undefined\n\n const abortPromise = new Promise<never>((_, reject) => {\n abortHandler = () => {\n reject(new errors.E_JOB_TIMEOUT([jobName, timeout]))\n }\n\n if (signal.aborted) {\n abortHandler()\n return\n }\n\n signal.addEventListener('abort', abortHandler, { once: true })\n })\n\n return {\n abortPromise,\n cleanupAbortListener: () => {\n if (abortHandler) {\n signal.removeEventListener('abort', abortHandler)\n }\n },\n }\n }\n\n async #acquireNextJob(queues: string[]): Promise<{ job: AcquiredJob; queue: string } | null> {\n for (const queue of queues) {\n const job = await this.#adapter.popFrom(queue)\n\n if (!job) {\n continue\n }\n\n debug('worker %s: acquired job %s', this.#id, job.id)\n return { job, queue }\n }\n\n return null\n }\n\n async #checkStalledJobs(queues: string[]): Promise<void> {\n const now = Date.now()\n\n // Only check if enough time has passed since last check\n if (now - this.#lastStalledCheck < this.#stalledInterval) {\n return\n }\n\n this.#lastStalledCheck = now\n\n for (const queue of queues) {\n const recovered = await this.#adapter.recoverStalledJobs(\n queue,\n this.#stalledThreshold,\n this.#maxStalledCount\n )\n\n if (recovered > 0) {\n debug('worker %s: recovered %d stalled jobs from queue %s', this.#id, recovered, queue)\n }\n }\n }\n\n #setupGracefulShutdown() {\n if (!this.#gracefulShutdown) {\n return\n }\n\n this.#shutdownHandler = async () => {\n debug('received shutdown signal, stopping worker...')\n\n if (this.#onShutdownSignal) {\n await this.#onShutdownSignal()\n }\n\n await this.stop()\n }\n\n process.on('SIGINT', this.#shutdownHandler)\n process.on('SIGTERM', this.#shutdownHandler)\n }\n\n #removeShutdownHandlers() {\n if (this.#shutdownHandler) {\n process.off('SIGINT', this.#shutdownHandler)\n process.off('SIGTERM', this.#shutdownHandler)\n this.#shutdownHandler = undefined\n }\n }\n\n /**\n * Dispatch any due scheduled jobs.\n *\n * Claims due schedules from the adapter and dispatches the corresponding\n * jobs to their configured queues.\n */\n async #dispatchDueSchedules(): Promise<void> {\n // Keep claiming due schedules until there are none left\n while (true) {\n const schedule = await this.#adapter.claimDueSchedule()\n\n if (!schedule) {\n break\n }\n\n debug(\n 'worker %s: dispatching scheduled job %s (schedule: %s, runCount: %d)',\n this.#id,\n schedule.name,\n schedule.id,\n schedule.runCount + 1\n )\n\n // Get the job class to determine the target queue\n const JobClass = Locator.get(schedule.name)\n const queue = JobClass?.options?.queue ?? 'default'\n\n // Dispatch the job to the queue\n await this.#adapter.pushOn(queue, {\n id: randomUUID(),\n name: schedule.name,\n payload: schedule.payload,\n attempts: 0,\n priority: JobClass?.options?.priority,\n })\n }\n }\n}\n","import type { AcquiredJob } from './contracts/adapter.js'\n\n/**\n * Entry representing an active job in the pool.\n */\ninterface PoolEntry {\n /** Promise that resolves when the job completes */\n promise: Promise<void>\n /** The acquired job data */\n job: AcquiredJob\n /** The queue this job came from */\n queue: string\n}\n\n/**\n * Manages concurrent job execution with a fixed pool size.\n *\n * The pool tracks running jobs and returns the first one to complete,\n * allowing maximum throughput regardless of individual job duration:\n *\n * ```\n * Job A: ████████████████████░░░░░░░░░░ (slow - 10s)\n * Job B: ████ done (fast - 100ms) ← returns first\n * Job C: ████████████░░░░░░░░░░░░░░░░░░ (medium - 2s)\n * ↑\n * Slot freed, new job can start immediately\n * ```\n *\n * Key insight: slow jobs don't block the pool. As soon as any job\n * completes, its slot becomes available for new work.\n */\nexport class JobPool {\n #activeJobs = new Map<string, PoolEntry>()\n\n /** Number of currently running jobs */\n get size() {\n return this.#activeJobs.size\n }\n\n /**\n * Check if the pool has no running jobs.\n *\n * @returns True if no jobs are running\n */\n isEmpty() {\n return this.#activeJobs.size === 0\n }\n\n /**\n * Check if the pool can accept more jobs.\n *\n * @param concurrency - Maximum number of concurrent jobs\n * @returns True if there's room for more jobs\n */\n hasCapacity(concurrency: number) {\n return this.#activeJobs.size < concurrency\n }\n\n /**\n * Add a job to the pool.\n *\n * @param job - The acquired job data\n * @param queue - The queue the job came from\n * @param promise - Promise that resolves when the job completes\n */\n add(job: AcquiredJob, queue: string, promise: Promise<void>) {\n this.#activeJobs.set(job.id, { promise, job, queue })\n }\n\n /**\n * Wait for the next job to complete and return it.\n *\n * Uses `Promise.race()` internally, so the fastest job wins.\n * The completed job is removed from the pool.\n *\n * @returns The first job to complete (success or failure)\n */\n async waitForNextCompletion(): Promise<PoolEntry> {\n const completedJobId = await Promise.race(\n [...this.#activeJobs.entries()].map(async ([id, { promise }]) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n return id\n })\n )\n\n const completed = this.#activeJobs.get(completedJobId)!\n this.#activeJobs.delete(completedJobId)\n\n return completed\n }\n\n /**\n * Wait for all running jobs to complete.\n *\n * Used during graceful shutdown to ensure no jobs are abandoned.\n * Clears the pool after all jobs finish.\n */\n async drain(): Promise<void> {\n const promises = [...this.#activeJobs.values()].map(async ({ promise }) => {\n try {\n await promise\n } catch {\n // Errors are handled in Worker#execute\n }\n })\n\n await Promise.all(promises)\n this.#activeJobs.clear()\n }\n}\n","import { QueueManager } from './queue_manager.js'\nimport { JobDispatcher } from './job_dispatcher.js'\nimport type { ScheduleData, ScheduleListOptions, ScheduleStatus } from './types/main.js'\n\n/**\n * Represents a persisted job schedule.\n *\n * Use `Schedule.find()` or `Schedule.list()` to retrieve schedules,\n * then use instance methods to manage them.\n *\n * @example\n * ```typescript\n * const schedule = await Schedule.find('cleanup-daily')\n * if (schedule) {\n * await schedule.pause()\n * // Later...\n * await schedule.resume()\n * }\n *\n * // List all active schedules\n * const activeSchedules = await Schedule.list({ status: 'active' })\n * ```\n */\nexport class Schedule {\n readonly #data: ScheduleData\n\n constructor(data: ScheduleData) {\n this.#data = data\n }\n\n get id(): string {\n return this.#data.id\n }\n\n get name(): string {\n return this.#data.name\n }\n\n get payload(): unknown {\n return this.#data.payload\n }\n\n get cronExpression(): string | null {\n return this.#data.cronExpression\n }\n\n get everyMs(): number | null {\n return this.#data.everyMs\n }\n\n get timezone(): string {\n return this.#data.timezone\n }\n\n get from(): Date | null {\n return this.#data.from\n }\n\n get to(): Date | null {\n return this.#data.to\n }\n\n get limit(): number | null {\n return this.#data.limit\n }\n\n get runCount(): number {\n return this.#data.runCount\n }\n\n get nextRunAt(): Date | null {\n return this.#data.nextRunAt\n }\n\n get lastRunAt(): Date | null {\n return this.#data.lastRunAt\n }\n\n get status(): ScheduleStatus {\n return this.#data.status\n }\n\n get createdAt(): Date {\n return this.#data.createdAt\n }\n\n /**\n * Find a schedule by ID.\n *\n * @param id - The schedule ID\n * @returns The schedule instance, or null if not found\n */\n static async find(id: string): Promise<Schedule | null> {\n const adapter = QueueManager.use()\n const data = await adapter.getSchedule(id)\n\n if (!data) return null\n\n return new Schedule(data)\n }\n\n /**\n * List all schedules matching the given options.\n *\n * @param options - Optional filters for listing\n * @returns Array of schedule instances\n */\n static async list(options?: ScheduleListOptions): Promise<Schedule[]> {\n const adapter = QueueManager.use()\n const schedules = await adapter.listSchedules(options)\n\n return schedules.map((data) => new Schedule(data))\n }\n\n /**\n * Pause this schedule.\n * No jobs will be dispatched while paused.\n */\n async pause(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'paused' })\n this.#data.status = 'paused'\n }\n\n /**\n * Resume this schedule.\n * Jobs will be dispatched according to the schedule.\n */\n async resume(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.updateSchedule(this.#data.id, { status: 'active' })\n this.#data.status = 'active'\n }\n\n /**\n * Delete this schedule permanently.\n */\n async delete(): Promise<void> {\n const adapter = QueueManager.use()\n await adapter.deleteSchedule(this.#data.id)\n }\n\n /**\n * Trigger immediate execution of this schedule's job.\n * Also updates runCount and lastRunAt.\n *\n * If the schedule has reached its limit, the job will not be dispatched.\n */\n async trigger(): Promise<void> {\n // Check if limit is reached\n if (this.#data.limit !== null && this.#data.runCount >= this.#data.limit) {\n return\n }\n\n const adapter = QueueManager.use()\n\n // Dispatch the job\n const dispatcher = new JobDispatcher(this.#data.name, this.#data.payload)\n await dispatcher.run()\n\n // Update run metadata\n const now = new Date()\n const newRunCount = this.#data.runCount + 1\n\n await adapter.updateSchedule(this.#data.id, {\n runCount: newRunCount,\n lastRunAt: now,\n })\n\n this.#data.runCount = newRunCount\n this.#data.lastRunAt = now\n }\n}\n","import type { Knex } from 'knex'\n\nexport class QueueSchemaService {\n #connection: Knex\n\n constructor(connection: Knex) {\n this.#connection = connection\n }\n\n /**\n * Creates the jobs table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createJobsTable(\n tableName: string = 'queue_jobs',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).notNullable()\n table.string('queue', 255).notNullable()\n table.enu('status', ['pending', 'active', 'delayed', 'completed', 'failed']).notNullable()\n table.text('data').notNullable()\n table.bigint('score').unsigned().nullable()\n table.string('worker_id', 255).nullable()\n table.bigint('acquired_at').unsigned().nullable()\n table.bigint('execute_at').unsigned().nullable()\n table.bigint('finished_at').unsigned().nullable()\n table.text('error').nullable()\n table.primary(['id', 'queue'])\n table.index(['queue', 'status', 'score'])\n table.index(['queue', 'status', 'execute_at'])\n table.index(['queue', 'status', 'finished_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Creates the schedules table with the default schema.\n * The optional callback allows adding custom columns.\n */\n async createSchedulesTable(\n tableName: string = 'queue_schedules',\n extend?: (table: Knex.CreateTableBuilder) => void\n ): Promise<void> {\n await this.#connection.schema.createTable(tableName, (table) => {\n table.string('id', 255).primary()\n table.string('status', 50).notNullable().defaultTo('active')\n table.string('name', 255).notNullable()\n table.text('payload').notNullable()\n table.string('cron_expression', 255).nullable()\n table.bigint('every_ms').unsigned().nullable()\n table.string('timezone', 100).notNullable().defaultTo('UTC')\n table.timestamp('from_date').nullable()\n table.timestamp('to_date').nullable()\n table.integer('run_limit').unsigned().nullable()\n table.integer('run_count').unsigned().notNullable().defaultTo(0)\n table.timestamp('next_run_at').nullable()\n table.timestamp('last_run_at').nullable()\n table\n .timestamp('created_at')\n .notNullable()\n .defaultTo(this.#connection.fn.now())\n table.index(['status', 'next_run_at'])\n\n extend?.(table)\n })\n }\n\n /**\n * Drops the jobs table if it exists.\n */\n async dropJobsTable(tableName: string = 'queue_jobs'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n\n /**\n * Drops the schedules table if it exists.\n */\n async dropSchedulesTable(tableName: string = 'queue_schedules'): Promise<void> {\n await this.#connection.schema.dropTableIfExists(tableName)\n }\n}\n","import type { BackoffConfig, Duration } from '../types/main.js'\nimport * as errors from '../exceptions.js'\nimport { parse } from '../utils.js'\nimport { RuntimeException } from '@poppinss/utils/exception'\nimport { assertUnreachable } from '@poppinss/utils/assert'\n\n/**\n * Calculates retry delays using configurable strategies.\n *\n * Supports three built-in strategies:\n * - `exponential`: Delay doubles each attempt (1s, 2s, 4s, 8s, ...)\n * - `linear`: Delay increases linearly (5s, 10s, 15s, 20s, ...)\n * - `fixed`: Same delay every time (10s, 10s, 10s, ...)\n *\n * All strategies support:\n * - `maxDelay`: Cap the maximum delay\n * - `jitter`: Add randomness to prevent thundering herd\n *\n * @example\n * ```typescript\n * const strategy = new BackoffStrategy({\n * strategy: 'exponential',\n * baseDelay: '1s',\n * maxDelay: '5m',\n * multiplier: 2,\n * jitter: true,\n * })\n *\n * strategy.calculateDelay(1) // ~1000ms\n * strategy.calculateDelay(2) // ~2000ms\n * strategy.calculateDelay(3) // ~4000ms\n * ```\n */\nexport class BackoffStrategy {\n readonly #config: BackoffConfig\n\n /**\n * Create a new backoff strategy.\n *\n * @param config - Backoff configuration\n * @throws {E_INVALID_BASE_DELAY} If baseDelay is not positive\n * @throws {E_INVALID_MAX_DELAY} If maxDelay is invalid\n * @throws {E_INVALID_MULTIPLIER} If multiplier is invalid\n */\n constructor(config: BackoffConfig) {\n this.#config = config\n this.#validateConfig()\n }\n\n /**\n * Calculate the delay for a given attempt number.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Delay in milliseconds\n * @throws {RuntimeException} If attempt is less than 1\n *\n * @example\n * ```typescript\n * // Exponential: 1s, 2s, 4s, 8s, 16s, ...\n * strategy.calculateDelay(1) // 1000\n * strategy.calculateDelay(2) // 2000\n * strategy.calculateDelay(3) // 4000\n * ```\n */\n calculateDelay(attempt: number): number {\n if (attempt < 1) {\n throw new RuntimeException('Attempt number must be >= 1')\n }\n\n const baseDelayMs = parse(this.#config.baseDelay)\n const maxDelayMs = this.#config.maxDelay ? parse(this.#config.maxDelay) : Infinity\n const multiplier = this.#config.multiplier ?? 2\n\n let delay: number\n\n switch (this.#config.strategy) {\n case 'exponential':\n delay = baseDelayMs * Math.pow(multiplier, attempt - 1)\n break\n case 'linear':\n delay = baseDelayMs * attempt\n break\n case 'fixed':\n delay = baseDelayMs\n break\n default:\n assertUnreachable(this.#config.strategy)\n }\n\n // Apply max delay limit\n delay = Math.min(delay, maxDelayMs)\n\n if (this.#config.jitter) {\n delay = this.#applyJitter(delay)\n }\n\n return Math.floor(delay)\n }\n\n /**\n * Get the Date when the next retry should occur.\n *\n * @param attempt - The attempt number (1-based)\n * @returns Date for the next retry\n *\n * @example\n * ```typescript\n * const nextRetry = strategy.getNextRetryAt(3)\n * console.log(`Retry at: ${nextRetry.toISOString()}`)\n * ```\n */\n getNextRetryAt(attempt: number): Date {\n const delay = this.calculateDelay(attempt)\n return new Date(Date.now() + delay)\n }\n\n /**\n * Get a frozen copy of the configuration.\n *\n * @returns Readonly configuration object\n */\n getConfig(): Readonly<BackoffConfig> {\n return Object.freeze({ ...this.#config })\n }\n\n #validateConfig() {\n const baseDelayMs = parse(this.#config.baseDelay)\n\n if (baseDelayMs <= 0) {\n throw new errors.E_INVALID_BASE_DELAY([\n 'Base delay must be a positive integer greater than zero',\n ])\n }\n\n if (this.#config.maxDelay) {\n const maxDelayMs = parse(this.#config.maxDelay)\n\n if (maxDelayMs <= 0) {\n throw new errors.E_INVALID_MAX_DELAY([\n 'Max delay must be a positive integer greater than zero',\n ])\n }\n\n if (maxDelayMs <= baseDelayMs) {\n throw new errors.E_INVALID_MAX_DELAY(['Max delay should be greater than base delay'])\n }\n }\n\n if (this.#config.multiplier !== undefined) {\n if (this.#config.multiplier <= 0) {\n throw new errors.E_INVALID_MULTIPLIER([\n 'Multiplier must be a positive number greater than zero',\n ])\n }\n\n if (this.#config.strategy === 'exponential' && this.#config.multiplier < 1) {\n throw new errors.E_INVALID_MULTIPLIER(['Exponential strategy multiplier should be >= 1'])\n }\n }\n }\n\n #applyJitter(delay: number): number {\n const jitterRange = delay * 0.25\n const jitter = (Math.random() - 0.5) * 2 * jitterRange\n\n return Math.max(0, delay + jitter)\n }\n}\n\n/**\n * Create an exponential backoff strategy factory.\n *\n * Delay doubles with each attempt: 1s → 2s → 4s → 8s → ...\n *\n * Default config:\n * - baseDelay: 1s\n * - maxDelay: 5m\n * - multiplier: 2\n * - jitter: true\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: exponentialBackoff({ baseDelay: '500ms', maxDelay: '1m' }),\n * },\n * }\n * ```\n */\nexport function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'exponential',\n baseDelay: '1s',\n maxDelay: '5m',\n multiplier: 2,\n jitter: true,\n ...config,\n })\n}\n\n/**\n * Create a linear backoff strategy factory.\n *\n * Delay increases linearly: 5s → 10s → 15s → 20s → ...\n *\n * Default config:\n * - baseDelay: 5s\n * - maxDelay: 2m\n *\n * @param config - Optional overrides for default config\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: linearBackoff({ baseDelay: '10s' }),\n * },\n * }\n * ```\n */\nexport function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'linear',\n baseDelay: '5s',\n maxDelay: '2m',\n ...config,\n })\n}\n\n/**\n * Create a fixed delay backoff strategy factory.\n *\n * Same delay every time: 10s → 10s → 10s → ...\n *\n * @param delay - The fixed delay (default: '10s')\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 3,\n * backoff: fixedBackoff('30s'),\n * },\n * }\n * ```\n */\nexport function fixedBackoff(delay: Duration = '10s') {\n return () =>\n new BackoffStrategy({\n strategy: 'fixed',\n baseDelay: delay,\n })\n}\n\n/**\n * Create a custom backoff strategy factory.\n *\n * Use this when you need full control over the configuration.\n *\n * @param config - Complete backoff configuration\n * @returns Factory function for creating BackoffStrategy instances\n *\n * @example\n * ```typescript\n * const config = {\n * retry: {\n * maxRetries: 5,\n * backoff: customBackoff({\n * strategy: 'exponential',\n * baseDelay: '100ms',\n * maxDelay: '30s',\n * multiplier: 3,\n * jitter: false,\n * }),\n * },\n * }\n * ```\n */\nexport function customBackoff(config: BackoffConfig) {\n return () => new BackoffStrategy(config)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AcquiredJob, A as Adapter } from '../../index-
|
|
1
|
+
export { b as AcquiredJob, A as Adapter } from '../../index-BAMFA6FI.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../index-
|
|
1
|
+
import { A as Adapter, J as JobData, a as JobClass, b as AcquiredJob, c as JobRetention, d as JobRecord, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../index-BAMFA6FI.js';
|
|
2
2
|
|
|
3
3
|
interface FakeJobRecord {
|
|
4
4
|
queue: string;
|
|
@@ -7,7 +7,7 @@ interface FakeJobRecord {
|
|
|
7
7
|
pushedAt: number;
|
|
8
8
|
}
|
|
9
9
|
type FakeJobMatcher = string | JobClass | ((job: JobData) => boolean);
|
|
10
|
-
type FakePayloadMatcher = ((payload:
|
|
10
|
+
type FakePayloadMatcher = ((payload: unknown) => boolean) | object | string | number | boolean | null | undefined;
|
|
11
11
|
type FakeDelayMatcher = number | ((delay: number | undefined) => boolean);
|
|
12
12
|
interface FakeJobQuery {
|
|
13
13
|
queue?: string;
|
|
@@ -51,6 +51,10 @@ declare class FakeAdapter implements Adapter {
|
|
|
51
51
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
52
52
|
getJob(jobId: string, queue: string): Promise<JobRecord | null>;
|
|
53
53
|
destroy(): Promise<void>;
|
|
54
|
+
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Use `upsertSchedule` instead.
|
|
57
|
+
*/
|
|
54
58
|
createSchedule(config: ScheduleConfig): Promise<string>;
|
|
55
59
|
getSchedule(id: string): Promise<ScheduleData | null>;
|
|
56
60
|
listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]>;
|