@boringnode/queue 0.5.0 → 0.5.2
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 +80 -6
- package/build/{chunk-WOUYSNK2.js → chunk-KI47AJ6U.js} +2 -2
- package/build/chunk-PZ5AY32C.js +10 -0
- package/build/chunk-PZ5AY32C.js.map +1 -0
- package/build/{chunk-ZZFSQY36.js → chunk-QEFYHCL7.js} +4 -6
- package/build/{chunk-ZZFSQY36.js.map → chunk-QEFYHCL7.js.map} +1 -1
- package/build/{chunk-OVYXMSSU.js → chunk-VRXHCWNK.js} +113 -31
- package/build/chunk-VRXHCWNK.js.map +1 -0
- package/build/chunk-WVLSICD4.js +20 -0
- package/build/chunk-WVLSICD4.js.map +1 -0
- package/build/index.d.ts +61 -10
- package/build/index.js +64 -51
- package/build/index.js.map +1 -1
- package/build/{index-B1XdqWpN.d.ts → job-Z5fBSzRX.d.ts} +160 -6
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +6 -1
- package/build/src/drivers/fake_adapter.js +4 -2
- package/build/src/drivers/knex_adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.js +2 -1
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +1 -1
- package/build/src/drivers/redis_adapter.js +92 -93
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +1 -1
- package/build/src/drivers/sync_adapter.js +38 -18
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/otel.d.ts +63 -0
- package/build/src/otel.js +245 -0
- package/build/src/otel.js.map +1 -0
- package/build/src/types/index.d.ts +6 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +34 -0
- package/build/src/types/tracing_channels.js +1 -0
- package/build/src/types/tracing_channels.js.map +1 -0
- package/package.json +36 -14
- package/build/chunk-OVYXMSSU.js.map +0 -1
- /package/build/{chunk-WOUYSNK2.js.map → chunk-KI47AJ6U.js.map} +0 -0
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport { JobExecutionRuntime } from './job_runtime.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 stopping job consumption.\n * Adapter cleanup remains the responsibility of `QueueManager.destroy()`.\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 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, context, payload } = await this.#initJob(job, queue)\n const configResolver = QueueManager.getConfigResolver()\n const retention = configResolver.resolveJobOptions(queue, options)\n const runtime = JobExecutionRuntime.from({\n jobName: job.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n\n try {\n await runtime.execute(instance, payload, context)\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 outcome = runtime.resolveFailure(e as Error, job.attempts)\n\n if (outcome.type === 'failed' && outcome.reason === 'timeout') {\n debug('worker %s: job %s timed out and failOnTimeout is set', this.#id, job.id)\n await this.#adapter.failJob(job.id, queue, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type === 'failed' && outcome.reason === 'no-retries') {\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, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type === 'failed' && outcome.reason === 'max-attempts') {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n runtime.maxRetries\n )\n await this.#adapter.failJob(job.id, queue, outcome.storageError, retention.removeOnFail)\n await instance.failed?.(outcome.hookError)\n\n return\n }\n\n if (outcome.type === 'retry' && outcome.retryAt) {\n debug('worker %s: job %s will retry at %s', this.#id, job.id, outcome.retryAt.toISOString())\n await this.#adapter.retryJob(job.id, queue, outcome.retryAt)\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 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\n return { instance, options, 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.getConfigResolver().resolveJobOptions(queue)\n await this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail)\n throw error\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
|
+
{"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 { QueueManager } from './queue_manager.js'\nimport { JobPool } from './job_pool.js'\nimport { JobExecutionRuntime } from './job_runtime.js'\nimport { dispatchChannel, executeChannel } from './tracing_channels.js'\nimport type { Adapter, AcquiredJob } from './contracts/adapter.js'\nimport type { JobContext, JobOptions, JobRetention, QueueManagerConfig, WorkerCycle } from './types/main.js'\nimport type { JobDispatchMessage, JobExecuteMessage } from './types/tracing_channels.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 #wrapInternal: <T>(fn: () => Promise<T>) => Promise<T> = (fn) => fn()\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 this.#wrapInternal = QueueManager.getInternalOperationWrapper()\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 stopping job consumption.\n * Adapter cleanup remains the responsibility of `QueueManager.destroy()`.\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 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, context, payload } = await this.#initJob(job, queue)\n const configResolver = QueueManager.getConfigResolver()\n const retention = configResolver.resolveJobOptions(queue, options)\n const runtime = JobExecutionRuntime.from({\n jobName: job.name,\n options,\n retryConfig: configResolver.resolveRetryConfig(queue, options),\n defaultTimeout: configResolver.getWorkerTimeout(),\n })\n\n const executeMessage: JobExecuteMessage = { job, queue }\n\n const run = () => {\n return executeChannel.tracePromise(async () => {\n try {\n await runtime.execute(instance, payload, context)\n await this.#wrapInternal(() => this.#adapter.completeJob(job.id, queue, retention.removeOnComplete))\n executeMessage.status = 'completed'\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, (performance.now() - startTime).toFixed(2))\n } catch (e) {\n await this.#handleExecutionFailure({ error: e as Error, job, queue, instance, runtime, retention, executeMessage })\n }\n\n executeMessage.duration = Number((performance.now() - startTime).toFixed(2))\n }, executeMessage)\n }\n\n const executionWrapper = QueueManager.getExecutionWrapper()\n await executionWrapper(run, job, queue)\n }\n\n async #handleExecutionFailure(options: {\n error: Error\n job: AcquiredJob\n queue: string\n instance: Job\n runtime: JobExecutionRuntime\n retention: { removeOnComplete?: JobRetention; removeOnFail?: JobRetention }\n executeMessage: JobExecuteMessage\n }) {\n const outcome = options.runtime.resolveFailure(options.error, options.job.attempts)\n options.executeMessage.error = options.error\n\n if (outcome.type === 'failed') {\n options.executeMessage.status = 'failed'\n await this.#wrapInternal(() =>\n this.#adapter.failJob(options.job.id, options.queue, outcome.storageError, options.retention.removeOnFail)\n )\n await options.instance.failed?.(outcome.hookError)\n return\n }\n\n if (outcome.type !== 'retry') return\n\n options.executeMessage.status = 'retrying'\n options.executeMessage.nextRetryAt = outcome.retryAt\n\n if (outcome.retryAt) {\n debug('worker %s: job %s will retry at %s', this.#id, options.job.id, outcome.retryAt.toISOString())\n await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue, outcome.retryAt))\n } else {\n await this.#wrapInternal(() => this.#adapter.retryJob(options.job.id, options.queue))\n }\n }\n\n async #initJob(\n job: AcquiredJob,\n queue: string\n ): Promise<{\n instance: Job\n options: JobOptions\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\n return { instance, options, 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.getConfigResolver().resolveJobOptions(queue)\n await this.#wrapInternal(() => this.#adapter.failJob(job.id, queue, error as Error, retention.removeOnFail))\n throw error\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.#wrapInternal(() => 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.#wrapInternal(() =>\n this.#adapter.recoverStalledJobs(queue, this.#stalledThreshold, 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.#wrapInternal(() => 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 const jobData = {\n id: randomUUID(),\n name: schedule.name,\n payload: schedule.payload,\n attempts: 0,\n priority: JobClass?.options?.priority,\n }\n\n const message: JobDispatchMessage = { jobs: [jobData], queue }\n await dispatchChannel.tracePromise(async () => {\n await this.#wrapInternal(() => this.#adapter.pushOn(queue, jobData))\n }, message)\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 * @param payload - Optional custom payload for the job\n */\n async trigger(payload?: any): 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, payload ?? 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":[]}
|
|
@@ -206,7 +206,13 @@ type Duration = number | string;
|
|
|
206
206
|
* - `{ age?, count? }`: Keep with pruning by age and/or count
|
|
207
207
|
*/
|
|
208
208
|
type JobRetention = boolean | {
|
|
209
|
+
/**
|
|
210
|
+
* Keep jobs newer than this duration.
|
|
211
|
+
*/
|
|
209
212
|
age?: Duration;
|
|
213
|
+
/**
|
|
214
|
+
* Keep at most this many jobs.
|
|
215
|
+
*/
|
|
210
216
|
count?: number;
|
|
211
217
|
};
|
|
212
218
|
/**
|
|
@@ -265,7 +271,7 @@ interface JobData {
|
|
|
265
271
|
/**
|
|
266
272
|
* Job priority (lower = higher priority).
|
|
267
273
|
*
|
|
268
|
-
* @default
|
|
274
|
+
* @default 5
|
|
269
275
|
*/
|
|
270
276
|
priority?: number;
|
|
271
277
|
/**
|
|
@@ -291,6 +297,16 @@ interface JobData {
|
|
|
291
297
|
* ```
|
|
292
298
|
*/
|
|
293
299
|
groupId?: string;
|
|
300
|
+
/**
|
|
301
|
+
* Timestamp (ms) when the job was dispatched.
|
|
302
|
+
* Used to compute queue wait time in OTel instrumentation.
|
|
303
|
+
*/
|
|
304
|
+
createdAt?: number;
|
|
305
|
+
/**
|
|
306
|
+
* Serialized trace context for distributed tracing.
|
|
307
|
+
* Injected by OTel plugin at dispatch time.
|
|
308
|
+
*/
|
|
309
|
+
traceContext?: Record<string, string>;
|
|
294
310
|
}
|
|
295
311
|
/**
|
|
296
312
|
* Record of a job's current state, including history for completed/failed jobs.
|
|
@@ -340,22 +356,28 @@ interface JobOptions {
|
|
|
340
356
|
queue?: string;
|
|
341
357
|
/**
|
|
342
358
|
* Adapter name or factory to use for this job.
|
|
359
|
+
*
|
|
360
|
+
* Defaults to the queue manager's configured default adapter.
|
|
343
361
|
*/
|
|
344
362
|
adapter?: string | (() => Adapter);
|
|
345
363
|
/**
|
|
346
364
|
* Maximum retry attempts before permanent failure.
|
|
347
365
|
*
|
|
348
|
-
*
|
|
366
|
+
* This is a convenience alias for `retry.maxRetries`.
|
|
367
|
+
*
|
|
368
|
+
* @default 0
|
|
349
369
|
*/
|
|
350
370
|
maxRetries?: number;
|
|
351
371
|
/**
|
|
352
372
|
* Job priority (lower = higher priority).
|
|
353
373
|
*
|
|
354
|
-
* @default
|
|
374
|
+
* @default 5
|
|
355
375
|
*/
|
|
356
376
|
priority?: number;
|
|
357
377
|
/**
|
|
358
|
-
* Retry configuration
|
|
378
|
+
* Retry configuration for this job.
|
|
379
|
+
*
|
|
380
|
+
* Overrides queue-level and global retry settings.
|
|
359
381
|
*/
|
|
360
382
|
retry?: RetryConfig;
|
|
361
383
|
/**
|
|
@@ -367,10 +389,22 @@ interface JobOptions {
|
|
|
367
389
|
/**
|
|
368
390
|
* Whether to mark job as failed on timeout.
|
|
369
391
|
*
|
|
370
|
-
*
|
|
392
|
+
* When disabled, timed out jobs follow the normal retry policy.
|
|
393
|
+
*
|
|
394
|
+
* @default false
|
|
371
395
|
*/
|
|
372
396
|
failOnTimeout?: boolean;
|
|
397
|
+
/**
|
|
398
|
+
* Retention policy for completed jobs.
|
|
399
|
+
*
|
|
400
|
+
* By default, completed jobs are removed immediately.
|
|
401
|
+
*/
|
|
373
402
|
removeOnComplete?: JobRetention;
|
|
403
|
+
/**
|
|
404
|
+
* Retention policy for failed jobs.
|
|
405
|
+
*
|
|
406
|
+
* By default, failed jobs are removed immediately after the failure hooks run.
|
|
407
|
+
*/
|
|
374
408
|
removeOnFail?: JobRetention;
|
|
375
409
|
}
|
|
376
410
|
/**
|
|
@@ -438,23 +472,77 @@ type JobClass<T extends Job = Job> = (new (...args: unknown[]) => T) & {
|
|
|
438
472
|
* ```
|
|
439
473
|
*/
|
|
440
474
|
type JobFactory = (JobClass: JobClass) => Job | Promise<Job>;
|
|
475
|
+
/**
|
|
476
|
+
* Retry policy used by jobs, queues, or the queue manager.
|
|
477
|
+
*/
|
|
441
478
|
interface RetryConfig {
|
|
479
|
+
/**
|
|
480
|
+
* Number of retry attempts after the first failed execution.
|
|
481
|
+
*
|
|
482
|
+
* Set to `0` to disable retries.
|
|
483
|
+
*
|
|
484
|
+
* @default 0
|
|
485
|
+
*/
|
|
442
486
|
maxRetries?: number;
|
|
487
|
+
/**
|
|
488
|
+
* Factory that creates the backoff strategy used between retry attempts.
|
|
489
|
+
*
|
|
490
|
+
* If omitted, failed jobs are retried as soon as the adapter makes them
|
|
491
|
+
* available again.
|
|
492
|
+
*/
|
|
443
493
|
backoff?: () => BackoffStrategy$1;
|
|
444
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* Built-in retry delay algorithms.
|
|
497
|
+
*/
|
|
445
498
|
type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
|
|
499
|
+
/**
|
|
500
|
+
* Configuration for built-in and custom retry backoff strategies.
|
|
501
|
+
*/
|
|
446
502
|
interface BackoffConfig {
|
|
503
|
+
/**
|
|
504
|
+
* Strategy used to compute the delay before the next retry.
|
|
505
|
+
*/
|
|
447
506
|
strategy: BackoffStrategy;
|
|
507
|
+
/**
|
|
508
|
+
* Initial delay used by the strategy.
|
|
509
|
+
*/
|
|
448
510
|
baseDelay: Duration;
|
|
511
|
+
/**
|
|
512
|
+
* Upper bound for computed retry delays.
|
|
513
|
+
*/
|
|
449
514
|
maxDelay?: Duration;
|
|
515
|
+
/**
|
|
516
|
+
* Growth factor for exponential backoff.
|
|
517
|
+
*/
|
|
450
518
|
multiplier?: number;
|
|
519
|
+
/**
|
|
520
|
+
* Whether to randomize retry delays to avoid retry bursts.
|
|
521
|
+
*/
|
|
451
522
|
jitter?: boolean;
|
|
452
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Runtime configuration for a named queue.
|
|
526
|
+
*/
|
|
453
527
|
interface QueueConfig {
|
|
528
|
+
/**
|
|
529
|
+
* Adapter name used by jobs dispatched to this queue.
|
|
530
|
+
*
|
|
531
|
+
* Falls back to the queue manager's default adapter.
|
|
532
|
+
*/
|
|
454
533
|
adapter?: string;
|
|
534
|
+
/**
|
|
535
|
+
* Retry policy applied to jobs in this queue unless overridden by job options.
|
|
536
|
+
*/
|
|
455
537
|
retry?: RetryConfig;
|
|
538
|
+
/**
|
|
539
|
+
* Default job options applied to jobs in this queue unless overridden by the job.
|
|
540
|
+
*/
|
|
456
541
|
defaultJobOptions?: JobOptions;
|
|
457
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Runtime options for workers that poll queues and execute jobs.
|
|
545
|
+
*/
|
|
458
546
|
interface WorkerConfig {
|
|
459
547
|
/**
|
|
460
548
|
* Maximum number of jobs to process concurrently.
|
|
@@ -503,22 +591,32 @@ interface WorkerConfig {
|
|
|
503
591
|
*/
|
|
504
592
|
onShutdownSignal?: () => void | Promise<void>;
|
|
505
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Event yielded by the low-level worker processing generator.
|
|
596
|
+
*/
|
|
506
597
|
type WorkerCycle = {
|
|
598
|
+
/** A job was acquired and execution started. */
|
|
507
599
|
type: 'started';
|
|
508
600
|
queue: string;
|
|
509
601
|
job: JobData;
|
|
510
602
|
} | {
|
|
603
|
+
/** A running job finished, either successfully or after failure handling. */
|
|
511
604
|
type: 'completed';
|
|
512
605
|
queue: string;
|
|
513
606
|
job: JobData;
|
|
514
607
|
} | {
|
|
608
|
+
/** No work was available. Consumers should wait before polling again. */
|
|
515
609
|
type: 'idle';
|
|
516
610
|
suggestedDelay: Duration;
|
|
517
611
|
} | {
|
|
612
|
+
/** An unexpected worker loop error occurred. */
|
|
518
613
|
type: 'error';
|
|
519
614
|
error: Error;
|
|
520
615
|
suggestedDelay: Duration;
|
|
521
616
|
};
|
|
617
|
+
/**
|
|
618
|
+
* Factory used to lazily create adapter instances.
|
|
619
|
+
*/
|
|
522
620
|
type AdapterFactory<T extends Adapter = Adapter> = () => T;
|
|
523
621
|
/**
|
|
524
622
|
* Status of a schedule.
|
|
@@ -597,13 +695,59 @@ interface ScheduleListOptions {
|
|
|
597
695
|
status?: ScheduleStatus;
|
|
598
696
|
}
|
|
599
697
|
interface QueueManagerConfig {
|
|
698
|
+
/**
|
|
699
|
+
* Name of the adapter used when a job does not select one explicitly.
|
|
700
|
+
*
|
|
701
|
+
* Must match one of the keys from `adapters`.
|
|
702
|
+
*/
|
|
600
703
|
default: string;
|
|
704
|
+
/**
|
|
705
|
+
* Available queue adapters keyed by name.
|
|
706
|
+
*
|
|
707
|
+
* Adapters are lazy-instantiated the first time they are used.
|
|
708
|
+
*/
|
|
601
709
|
adapters: Record<string, AdapterFactory>;
|
|
710
|
+
/**
|
|
711
|
+
* Global retry configuration applied to all jobs unless overridden by
|
|
712
|
+
* queue-level or job-level options.
|
|
713
|
+
*/
|
|
602
714
|
retry?: RetryConfig;
|
|
715
|
+
/**
|
|
716
|
+
* Global job options applied to all jobs unless overridden by queue-level
|
|
717
|
+
* or job-level options.
|
|
718
|
+
*/
|
|
603
719
|
defaultJobOptions?: JobOptions;
|
|
720
|
+
/**
|
|
721
|
+
* Per-queue configuration keyed by queue name.
|
|
722
|
+
*
|
|
723
|
+
* Use this to select adapters or defaults for specific queues.
|
|
724
|
+
*/
|
|
604
725
|
queues?: Record<string, QueueConfig>;
|
|
726
|
+
/**
|
|
727
|
+
* Worker runtime options used by `Worker` instances.
|
|
728
|
+
*/
|
|
605
729
|
worker?: WorkerConfig;
|
|
730
|
+
/**
|
|
731
|
+
* Glob patterns used to discover and register job classes.
|
|
732
|
+
*
|
|
733
|
+
* These locations are used by `init()` when `autoLoadJobs` is enabled,
|
|
734
|
+
* and by `QueueManager.loadJobs()` when called without arguments.
|
|
735
|
+
*/
|
|
606
736
|
locations?: string[];
|
|
737
|
+
/**
|
|
738
|
+
* Whether `init()` should immediately register jobs from configured locations.
|
|
739
|
+
*
|
|
740
|
+
* Framework integrations may disable this to defer job loading until a
|
|
741
|
+
* command lifecycle is ready, then call `QueueManager.loadJobs()`.
|
|
742
|
+
*
|
|
743
|
+
* @default true
|
|
744
|
+
*/
|
|
745
|
+
autoLoadJobs?: boolean;
|
|
746
|
+
/**
|
|
747
|
+
* Logger used by the queue runtime.
|
|
748
|
+
*
|
|
749
|
+
* Defaults to the console logger.
|
|
750
|
+
*/
|
|
607
751
|
logger?: Logger;
|
|
608
752
|
/**
|
|
609
753
|
* Custom factory function for job instantiation.
|
|
@@ -625,6 +769,16 @@ interface QueueManagerConfig {
|
|
|
625
769
|
* ```
|
|
626
770
|
*/
|
|
627
771
|
jobFactory?: JobFactory;
|
|
772
|
+
/**
|
|
773
|
+
* Wraps internal adapter operations (Redis, Knex calls) to suppress
|
|
774
|
+
* or customize instrumentation. Used by OTel to suppress child spans.
|
|
775
|
+
*/
|
|
776
|
+
internalOperationWrapper?: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
777
|
+
/**
|
|
778
|
+
* Wraps job execution to inject tracing context or custom behavior.
|
|
779
|
+
* Called around `runtime.execute()` for each job attempt.
|
|
780
|
+
*/
|
|
781
|
+
executionWrapper?: <T>(fn: () => Promise<T>, job: AcquiredJob, queue: string) => Promise<T>;
|
|
628
782
|
}
|
|
629
783
|
|
|
630
784
|
/**
|
|
@@ -1436,4 +1590,4 @@ declare abstract class Job<Payload = any> {
|
|
|
1436
1590
|
failed?(error: Error): Promise<void>;
|
|
1437
1591
|
}
|
|
1438
1592
|
|
|
1439
|
-
export { type Adapter as A, type
|
|
1593
|
+
export { type Adapter as A, type BackoffConfig as B, type Duration as D, type JobData as J, type Logger as L, type QueueManagerConfig as Q, type RetryConfig as R, type ScheduleConfig as S, type WorkerCycle as W, type JobClass as a, type AcquiredJob as b, type JobRetention as c, type JobRecord as d, type ScheduleData as e, type ScheduleListOptions as f, type JobOptions as g, type QueueConfig as h, type JobFactory as i, Job as j, type ScheduleStatus as k, ScheduleBuilder as l, JobBatchDispatcher as m, customBackoff as n, linearBackoff as o, exponentialBackoff as p, fixedBackoff as q, type AdapterFactory as r, type BackoffStrategy as s, type DispatchManyResult as t, type DispatchResult as u, type JobContext as v, type JobStatus as w, type ScheduleResult as x, type WorkerConfig as y };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { b as AcquiredJob, A as Adapter } from '../../
|
|
1
|
+
export { b as AcquiredJob, A as Adapter } from '../../job-Z5fBSzRX.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 '../../
|
|
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 '../../job-Z5fBSzRX.js';
|
|
2
2
|
|
|
3
3
|
interface FakeJobRecord {
|
|
4
4
|
queue: string;
|
|
@@ -23,6 +23,11 @@ declare function fake(): () => FakeAdapter;
|
|
|
23
23
|
*/
|
|
24
24
|
declare class FakeAdapter implements Adapter {
|
|
25
25
|
#private;
|
|
26
|
+
/**
|
|
27
|
+
* Set the function to call when the fake is disposed
|
|
28
|
+
*/
|
|
29
|
+
onDispose(fn: () => void): this;
|
|
30
|
+
[Symbol.dispose](): void;
|
|
26
31
|
setWorkerId(_workerId: string): void;
|
|
27
32
|
getPushedJobs(): FakeJobRecord[];
|
|
28
33
|
getPushedJobsOn(queue: string): FakeJobRecord[];
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FakeAdapter,
|
|
3
3
|
fake
|
|
4
|
-
} from "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
4
|
+
} from "../../chunk-VRXHCWNK.js";
|
|
5
|
+
import "../../chunk-WVLSICD4.js";
|
|
6
|
+
import "../../chunk-QEFYHCL7.js";
|
|
7
|
+
import "../../chunk-PZ5AY32C.js";
|
|
6
8
|
export {
|
|
7
9
|
FakeAdapter,
|
|
8
10
|
fake
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Knex } from 'knex';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-Z5fBSzRX.js';
|
|
3
3
|
|
|
4
4
|
interface KnexAdapterOptions {
|
|
5
5
|
connection: Knex;
|