/// /// /// import type { EventEmitter } from "events"; import type { Stats } from "fs"; import { AsyncHooks, Middleware } from "graphile-config"; import type { Notification, Pool, PoolClient, QueryResult, QueryResultRow } from "pg"; import type { CompiledSharedOptions, Release, ResolvedWorkerPreset } from "./lib"; import { LocalQueue } from "./localQueue"; import type { Logger } from "./logger"; import type { Signal } from "./signals"; export interface WithPgClient { (callback: (pgClient: PoolClient) => Promise): Promise; } export interface EnhancedWithPgClient extends WithPgClient { /** **Experimental**; see https://github.com/graphile/worker/issues/387 */ withRetries: WithPgClient; } /** * The `addJob` interface is implemented in many places in the library, all * conforming to this. */ export type AddJobFunction = ( /** * The name of the task that will be executed for this job. */ identifier: TIdentifier, /** * The payload (typically a JSON object) that will be passed to the task executor. */ payload: TIdentifier extends keyof GraphileWorker.Tasks ? GraphileWorker.Tasks[TIdentifier] : unknown, /** * Additional details about how the job should be handled. */ spec?: TaskSpec) => Promise; export interface AddJobsJobSpec { /** * The name of the task that will be executed for this job. */ identifier: TIdentifier; /** * The payload (typically a JSON object) that will be passed to the task executor. */ payload: TIdentifier extends keyof GraphileWorker.Tasks ? GraphileWorker.Tasks[TIdentifier] : unknown; /** * The queue to run this task under (only specify if you want jobs in this * queue to run serially). Avoid using high cardinality values (e.g., random * strings, UUIDs, timestamps) as this degrades performance and requires * periodic database cleanup. (Default: null) */ queueName?: string; /** * A Date to schedule this task to run in the future. (Default: now) */ runAt?: Date; /** * Jobs are executed in numerically ascending order of priority (jobs with a * numerically smaller priority are run first). (Default: 0) */ priority?: number; /** * How many retries should this task get? (Default: 25) */ maxAttempts?: number; /** * Unique identifier for the job, can be used to update or remove it later if * needed. (Default: null) */ jobKey?: string; /** * Flags for the job, can be used to dynamically filter which jobs can and * cannot run at runtime. (Default: null) */ flags?: string[]; /** Must NOT set jobKeyMode for addJobs; use addJob if you need that. */ jobKeyMode?: never; } /** * The `addJobs` interface is implemented in many places in the library, all * conforming to this. * * @experimental */ export type AddJobsFunction = (jobSpecs: TSpecs, jobKeyPreserveRunAt?: boolean) => Promise>; export interface Helpers { /** * A `Logger` instance. */ logger: Logger; /** * Grabs a PostgreSQL client from the pool, awaits your callback, then * releases the client back to the pool. */ withPgClient: WithPgClient; /** * Adds a job into the Graphile Worker queue. */ addJob: AddJobFunction; /** * Adds multiple jobs into the Graphile Worker queue. */ addJobs: AddJobsFunction; } export interface JobHelpers extends Helpers { /** * A `Logger` instance, scoped to this job. */ logger: Logger; /** * The whole, currently executing job. */ job: Job; /** * Get the queue name of the given queue ID (or of the currently executing job * if no queue ID is specified). */ getQueueName(queueId?: number | null): PromiseOrDirect; /** * A shorthand for running an SQL query within the job. */ query(queryText: string, values?: unknown[]): Promise>; /** * An `AbortSignal` that will be triggered when the job should exit. It is used, * for example, for a graceful shutdown request. * * @experimental */ abortSignal: AbortSignal; /** * A promise that rejects when the AbortSignal aborts. * * @experimental */ abortPromise: Promise; } export type CleanupTask = "GC_TASK_IDENTIFIERS" | "GC_JOB_QUEUES" | "DELETE_PERMAFAILED_JOBS"; export interface CleanupOptions { tasks?: readonly CleanupTask[]; taskIdentifiersToKeep?: readonly string[]; } /** * Utilities for working with Graphile Worker. Primarily useful for migrating * the jobs database and queueing jobs. */ export interface WorkerUtils extends Helpers { /** * A Logger instance, scoped to label: 'WorkerUtils' */ logger: Logger; /** * Use this to release the WorkerUtils when you no longer need it. * Particularly useful in tests, or in short-running scripts. */ release: Release; /** * Migrate the database schema to the latest version. */ migrate: () => Promise; /** * Marks the specified jobs (by their ids) as if they were completed, * assuming they are not locked. Note that completing a job deletes it. You * may mark failed and permanently failed jobs as completed if you wish. The * deleted jobs will be returned (note that this may be fewer jobs than you * requested). */ completeJobs: (ids: string[]) => Promise; /** * Marks the specified jobs (by their ids) as failed permanently, assuming * they are not locked. This means setting their `attempts` equal to their * `max_attempts`. The updated jobs will be returned (note that this may be * fewer jobs than you requested). */ permanentlyFailJobs: (ids: string[], reason?: string) => Promise; /** * Updates the specified scheduling properties of the jobs (assuming they are * not locked). All of the specified options are optional, omitted or null * values will left unmodified. * * This method can be used to postpone or advance job execution, or to * schedule a previously failed or permanently failed job for execution. The * updated jobs will be returned (note that this may be fewer jobs than you * requested). */ rescheduleJobs: (ids: string[], options: { runAt?: string | Date; priority?: number; attempts?: number; maxAttempts?: number; }) => Promise; /** * Forcefully unlocks jobs for the given workers, leaving others unaffected. * Only use this if the workers in question are no longer running (crashed, * were terminated, are permanently unreachable, etc). */ forceUnlockWorkers: (workerIds: string[]) => Promise; /** * **Experimental** * * Database cleanup function. Supported tasks: * * - GC_TASK_IDENTIFIERS: delete task identifiers that are no longer referenced by any jobs * - GC_JOB_QUEUES: delete job queues that are no longer referenced by any jobs * - DELETE_PERMAFAILED_JOBS: delete permanently failed jobs if they are not locked * * Default: ["GC_JOB_QUEUES"] */ cleanup(options: CleanupOptions): Promise; } export type PromiseOrDirect = Promise | T; export type Task = (payload: TName extends keyof GraphileWorker.Tasks ? GraphileWorker.Tasks[TName] : unknown, helpers: JobHelpers) => PromiseOrDirect[]>; export declare function isValidTask(fn: unknown): fn is Task; export type TaskList = { [Key in keyof GraphileWorker.Tasks | (string & {})]?: Key extends keyof GraphileWorker.Tasks ? Task : Task; }; export interface WatchedTaskList { tasks: TaskList; release: () => void; /** @internal */ compiledSharedOptions: CompiledSharedOptions; } export interface WatchedCronItems { items: Array; release: () => void; } /** * a.k.a. `opts`, this allows you to change the behaviour when scheduling a cron task. */ export interface CronItemOptions { /** How far back (in milliseconds) should we backfill jobs when worker starts? (Only backfills since when the identifier was first used.) */ backfillPeriod?: number; /** Optionally override the default job max_attempts */ maxAttempts?: number; /** Optionally set the job queue_name to enforce that the jobs run serially. * Avoid using high cardinality values (e.g., random strings, UUIDs, timestamps) * as this degrades performance and requires periodic database cleanup. */ queueName?: string; /** Optionally set the job priority */ priority?: number; /** Optionally prevent duplicate copies of this job from running */ jobKey?: string; /** * Modifies the behavior of `jobKey`; when 'replace' all attributes will be * updated, when 'preserve_run_at' all attributes except 'run_at' will be * updated. (Default: 'replace') */ jobKeyMode?: "replace" | "preserve_run_at"; } export interface ParsedCronItemOptions extends CronItemOptions { backfillPeriod: number; } /** * Crontab ranges from the minute, hour, day of month, month and day of week * parts of the crontab line * * @internal * * You should use this as an opaque type; you should **not** read values from * inside it, and you should not construct it manually. The definition of this * type may change dramatically between minor releases of Graphile Worker, * these changes are not seen as breaking changes. * * **WARNING**: it is assumed that values of this type adhere to the constraints in * the comments below (many of these cannot be asserted by TypeScript). If you * construct this type manually and do not adhere to these constraints then you * may get unexpected behaviours. Graphile Worker enforces these rules when * constructing `ParsedCronMatch`s internally, you should use the Graphile * Worker helpers to construct this type. */ export interface ParsedCronMatch { /** Minutes (0-59) on which to run the item; must contain unique numbers from the allowed range, ordered ascending. */ minutes: number[]; /** Hours (0-23) on which to run the item; must contain unique numbers from the allowed range, ordered ascending. */ hours: number[]; /** Dates (1-31) on which to run the item; must contain unique numbers from the allowed range, ordered ascending. */ dates: number[]; /** Months (1-12) on which to run the item; must contain unique numbers from the allowed range, ordered ascending. */ months: number[]; /** Days of the week (0-6) on which to run the item; must contain unique numbers from the allowed range, ordered ascending. */ dows: number[]; } /** * A function which determines if a particular item should be executed for a * given TimestampDigest. */ export type CronMatcher = (digest: TimestampDigest) => boolean; /** * Symbol to determine that the item was indeed fed through a parser function. * * @internal */ export declare const $$isParsed: unique symbol; /** * A recurring task schedule; this may represent a line in the `crontab` file, * or may be the result of calling `parseCronItems` on a list of `CronItem`s * the user has specified. * * You should use this as an opaque type; you should **not** read values from * inside it, and you should not construct it manually, use `parseCrontab` or * `parseCronItems` instead. The definition of this type may change * dramatically between minor releases of Graphile Worker, these changes are * not seen as breaking changes. */ export interface ParsedCronItem { /** @internal Used to guarantee that the item was parsed correctly */ [$$isParsed]: true; /** Optimised function to determine if this item matches the given TimestampDigest */ match: CronMatcher; /** The identifier of the task to execute */ task: string; /** Options influencing backfilling and properties of the scheduled job */ options: ParsedCronItemOptions; /** A payload object to merge into the default cron payload object for the scheduled job */ payload: { [key: string]: unknown; } | null; /** An identifier so that we can prevent double-scheduling of a task and determine whether or not to backfill. */ identifier: string; } /** * A description of a cron item detailing a task to run, when to run it, and * any additional options necessary. This is the human-writable form, it must * be parsed via `parseCronItems` before being fed to a worker. (ParsedCronItem * has strict rules and should only be constructed via Graphile Worker's * helpers to ensure compliance.) */ export interface CronItem { /** The identifier of the task to execute */ task: string; /** @deprecated Please rename this property to 'match' */ pattern?: never; /** Cron pattern (e.g. `* * * * *`) or a function to detail when the task should be executed */ match: string | CronMatcher; /** Options influencing backfilling and properties of the scheduled job */ options?: CronItemOptions; /** A payload object to merge into the default cron payload object for the scheduled job */ payload?: { [key: string]: unknown; }; /** An identifier so that we can prevent double-scheduling of a task and determine whether or not to backfill. */ identifier?: string; } /** Represents records in the `jobs` table */ export interface DbJob { id: string; /** FK to job_queues */ job_queue_id: number | null; /** FK to tasks */ task_id: number; /** The JSON payload of the job */ payload: unknown; /** Lower number means it should run sooner */ priority: number; /** When it was due to run */ run_at: Date; /** How many times it has been attempted */ attempts: number; /** The limit for the number of times it should be attempted */ max_attempts: number; /** If attempts > 0, why did it fail last? */ last_error: string | null; created_at: Date; updated_at: Date; /** "job_key" - unique identifier for easy update from user code */ key: string | null; /** A count of the revision numbers */ revision: number; locked_at: Date | null; locked_by: string | null; flags: { [flag: string]: true; } | null; is_available: boolean; } export interface Job extends DbJob { /** Shortcut to tasks.identifier */ task_identifier: string; } /** Represents records in the `known_crontabs` table */ export interface KnownCrontab { identifier: string; known_since: Date; last_execution: Date | null; } export interface Worker { workerPool: WorkerPool; nudge: () => boolean; workerId: string; release: (force?: boolean) => void | Promise; promise: Promise; getActiveJob: () => Job | null; /** @internal */ _start: (() => void) | null; } export interface WorkerPool { id: string; /** Encourage `n` workers to look for jobs _right now_, cancelling the delay timers. */ nudge(n: number): void; /** @deprecated Use gracefulShutdown instead */ release: () => PromiseOrDirect; gracefulShutdown: (message?: string) => PromiseOrDirect; forcefulShutdown: (message: string) => PromiseOrDirect<{ forceFailedJobs: readonly Job[]; }>; promise: Promise; /** Fires 'abort' when all running jobs should stop because worker is shutting down. @experimental */ abortSignal: AbortSignal; /** Rejects when the abortSignal aborts. @experimental */ abortPromise: Promise; /** @internal */ _shuttingDown: boolean; /** @internal */ _forcefulShuttingDown: boolean; /** @internal */ _active: boolean; /** @internal */ _workers: Worker[]; /** @internal */ _withPgClient: WithPgClient; /** @internal */ _start: (() => void) | null; /** * Only works if concurrency === 1! * * @internal */ worker: Worker | null; then(onfulfilled?: ((value: void) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike) | undefined | null): Promise; catch(onrejected?: ((reason: unknown) => TResult | PromiseLike) | undefined | null): Promise; finally(onfinally?: (() => void) | undefined | null): Promise; } export interface Runner { /** Attempts to cleanly shut down the runner */ stop: (reason?: string) => Promise; /** Use .stop() instead, unless you know what you're doing */ kill: (reason?: string) => Promise; addJob: AddJobFunction; promise: Promise; events: WorkerEvents; } export interface Cron { release(): Promise; promise: Promise; /** @internal */ _active: boolean; } export interface TaskSpec { /** * The queue to run this task under (only specify if you want jobs in this * queue to run serially). Avoid using high cardinality values (e.g., random * strings, UUIDs, timestamps) as this degrades performance and requires * periodic database cleanup. (Default: null) */ queueName?: string; /** * A Date to schedule this task to run in the future. (Default: now) */ runAt?: Date; /** * Jobs are executed in numerically ascending order of priority (jobs with a * numerically smaller priority are run first). (Default: 0) */ priority?: number; /** * How many retries should this task get? (Default: 25) */ maxAttempts?: number; /** * Unique identifier for the job, can be used to update or remove it later if * needed. (Default: null) */ jobKey?: string; /** * Modifies the behavior of `jobKey`; when 'replace' all attributes will be * updated, when 'preserve_run_at' all attributes except 'run_at' will be * updated, when 'unsafe_dedupe' a new job will only be added if no existing * job (including locked jobs and permanently failed jobs) with matching job * key exists. (Default: 'replace') */ jobKeyMode?: "replace" | "preserve_run_at" | "unsafe_dedupe"; /** * Flags for the job, can be used to dynamically filter which jobs can and * cannot run at runtime. (Default: null) */ flags?: string[]; } /** Equivalent of graphile_worker.job_spec DB type */ export interface DbJobSpec { identifier: TIdentifier; payload: TIdentifier extends keyof GraphileWorker.Tasks ? GraphileWorker.Tasks[TIdentifier] : unknown; queue_name?: string | null; run_at?: string | null; max_attempts?: number | null; job_key?: string | null; priority?: number | null; flags?: string[] | null; } export type ForbiddenFlagsFn = () => null | string[] | Promise; /** * These options are common Graphile Worker pools, workers, and utils. */ export interface SharedOptions { /** * How should messages be logged out? Defaults to using the console logger. */ logger?: Logger; /** * Which PostgreSQL schema should Graphile Worker use? Defaults to 'graphile_worker'. */ schema?: string; /** * A PostgreSQL connection string to the database containing the job queue */ connectionString?: string; /** * The maximum size of the PostgreSQL pool. Defaults to the node-postgres * default (10). Only useful when `connectionString` is given. */ maxPoolSize?: number; /** * A pg.Pool instance to use instead of the `connectionString` */ pgPool?: Pool; /** * Set true if you want to prevent the use of prepared statements; for * example if you wish to use Graphile Worker with a connection pool that * does not support prepared statements. */ noPreparedStatements?: boolean; /** * An array of strings or function returning an array of strings or promise resolving to * an array of strings that represent flags * * Graphile worker will skip the execution of any jobs that contain these flags */ forbiddenFlags?: null | string[] | ForbiddenFlagsFn; /** * An EventEmitter instance to which we'll emit events. */ events?: WorkerEvents; /** * By default we use PostgreSQL's time source; in general this should be pretty close * (if not identical) to the time source your Node server is using, but in the case * that it isn't or you want to change it (e.g. in tests with fake date/time) you can * tell Worker to use Node's time source rather than Postgres' time source. (Default: * false.) */ useNodeTime?: boolean; /** * **Experimental** * * How often should we scan for jobs that have been locked too long and * release them? This is the minimum interval, we'll choose a time between * this and `maxResetLockedInterval`. */ minResetLockedInterval?: number; /** * **Experimental** * * The upper bound of how long we'll wait between scans for jobs that have * been locked too long. See `minResetLockedInterval`. */ maxResetLockedInterval?: number; preset?: GraphileConfig.Preset; /** * How long in milliseconds after a gracefulShutdown is triggered should * we wait to trigger the AbortController, which should cancel supported * asynchronous actions? * * @defaultValue `5000` */ gracefulShutdownAbortTimeout?: number; } /** * Shared between pools and individual workers. */ export interface WorkerSharedOptions extends SharedOptions { /** * How long to wait between polling for jobs in milliseconds (for jobs scheduled in the future/retries) */ pollInterval?: number; } /** * Options for an individual worker */ export interface WorkerOptions extends WorkerSharedOptions { /** * An identifier for this specific worker; if unset then a random ID will be assigned. Do not assign multiple workers the same worker ID! */ workerId?: string; abortSignal: AbortSignal; workerPool: WorkerPool; /** * If set true, we won't install signal handlers and it'll be up to you to * handle graceful shutdown of the worker if the process receives a signal. */ noHandleSignals?: boolean; /** If false, worker won't start looking for jobs until you call `worker._start()` */ autostart?: boolean; } /** * Options for an individual worker */ export interface RunOnceOptions extends SharedOptions { /** * An identifier for this specific worker; if unset then a random ID will be assigned. Do not assign multiple workers the same worker ID! */ workerId?: string; /** * If set true, we won't install signal handlers and it'll be up to you to * handle graceful shutdown of the worker if the process receives a signal. */ noHandleSignals?: boolean; /** Single worker only! */ concurrency?: 1; } /** * Options for a worker pool. */ export interface WorkerPoolOptions extends WorkerSharedOptions { /** * Number of jobs to run concurrently */ concurrency?: number; /** * If set true, we won't install signal handlers and it'll be up to you to * handle graceful shutdown of the worker if the process receives a signal. */ noHandleSignals?: boolean; } /** * Options for the `run`, `runOnce` and `runMigrations` methods. */ export interface RunnerOptions extends WorkerPoolOptions { /** * Task names and handler, e.g. from `getTasks`. Overrides `taskDirectory` */ taskList?: TaskList; /** * Each file in this directory will be used as a task handler */ taskDirectory?: string; /** * A crontab string to use instead of reading a crontab file. Overrides * `crontabFile` */ crontab?: string; /** * Path to the crontab file. Defaults to `crontab` */ crontabFile?: string; /** * Programmatically generated cron items. **BE VERY CAREFUL** if you use this * manually, there are requirements on this type that TypeScript cannot * express, and if you don't adhere to them then you'll get unexpected * behaviours. Overrides `crontabFile` */ parsedCronItems?: Array; } /** Spec for a job created from cron */ export interface CronJob { task: string; payload: { _cron: { ts: string; backfilled?: boolean; }; [key: string]: unknown; }; queueName?: string; runAt: string; jobKey?: string; jobKeyMode: CronItemOptions["jobKeyMode"]; maxAttempts?: number; priority?: number; } export interface JobAndCronIdentifier { job: CronJob; identifier: string; } export interface JobAndCronIdentifierWithDetails extends JobAndCronIdentifier { known_since: Date; last_execution: Date | null; } export interface WorkerUtilsOptions extends SharedOptions { } type BaseEventMap = Record; type EventMapKey = string & keyof TEventMap; type EventCallback = (params: TPayload) => void; interface TypedEventEmitter extends EventEmitter { addListener>(eventName: TEventName, callback: EventCallback): this; on>(eventName: TEventName, callback: EventCallback): this; once>(eventName: TEventName, callback: EventCallback): this; removeListener>(eventName: TEventName, callback: EventCallback): this; off>(eventName: TEventName, callback: EventCallback): this; emit>(eventName: TEventName, params: TEventMap[TEventName]): boolean; } /** * These are the events that a worker instance supports. */ export type WorkerEventMap = { /** * When a worker pool is created */ "pool:create": { ctx: WorkerPluginContext; workerPool: WorkerPool; }; /** * When a worker pool attempts to connect to PG ready to issue a LISTEN * statement */ "pool:listen:connecting": { ctx: WorkerPluginContext; workerPool: WorkerPool; attempts: number; }; /** * When a worker pool starts listening for jobs via PG LISTEN */ "pool:listen:success": { ctx: WorkerPluginContext; workerPool: WorkerPool; client: PoolClient; }; /** * When a worker pool faces an error on their PG LISTEN client */ "pool:listen:error": { ctx: WorkerPluginContext; workerPool: WorkerPool; error: unknown; }; /** * When a worker pool receives a notification */ "pool:listen:notification": { ctx: WorkerPluginContext; workerPool: WorkerPool; message: Notification; client: PoolClient; }; /** * When a worker pool listening client is no longer available */ "pool:listen:release": { ctx: WorkerPluginContext; workerPool: WorkerPool; /** If you use this client, be careful to handle errors - it may be in an invalid state (errored, disconnected, etc). */ client: PoolClient; }; /** * When a worker pool fails to complete/fail a job */ "pool:fatalError": { ctx: WorkerPluginContext; workerPool: WorkerPool; error: unknown; action: string; }; /** * When a worker pool is released */ "pool:release": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; }; /** * When a worker pool starts a graceful shutdown */ "pool:gracefulShutdown": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; message: string; }; /** * When a worker pool graceful shutdown throws an error */ "pool:gracefulShutdown:error": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; error: unknown; }; /** * When a worker pool graceful shutdown is successful, but one of the workers * throws an error from release() */ "pool:gracefulShutdown:workerError": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; error: unknown; job: Job | null; }; /** * When a worker pool graceful shutdown throws an error */ "pool:gracefulShutdown:complete": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; }; /** * When a worker pool starts a forceful shutdown */ "pool:forcefulShutdown": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; message: string; }; /** * When a worker pool forceful shutdown throws an error */ "pool:forcefulShutdown:error": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; error: unknown; }; /** * When a worker pool forceful shutdown throws an error */ "pool:forcefulShutdown:complete": { ctx: WorkerPluginContext; /** @deprecated Use workerPool for consistency */ pool: WorkerPool; workerPool: WorkerPool; }; /** * When a local queue is created */ "localQueue:init": { ctx: WorkerPluginContext; localQueue: LocalQueue; }; /** * When a local queue enters 'polling' mode */ "localQueue:setMode": { ctx: WorkerPluginContext; localQueue: LocalQueue; oldMode: LocalQueueMode; newMode: Exclude; }; /** * Too few jobs were fetched from the DB, so the local queue is going to * sleep. */ "localQueue:refetchDelay:start": { ctx: WorkerPluginContext; localQueue: LocalQueue; /** The number of jobs that were fetched */ jobCount: number; /** We needed this number or fewer jobs to trigger */ threshold: number; /** How long we should delay for */ delayMs: number; /** If we receive this number of nudges, we will abort the delay */ abortThreshold: number; }; /** * Too many nudges happened whilst the local queue was asleep, and it has * been awoken early to deal with the rush! */ "localQueue:refetchDelay:abort": { ctx: WorkerPluginContext; localQueue: LocalQueue; /** How many nudges did we receive during the delay */ count: number; /** How many nudges did we need to receive for the abort */ abortThreshold: number; }; /** * The refetchDelay terminated normally. */ "localQueue:refetchDelay:expired": { ctx: WorkerPluginContext; localQueue: LocalQueue; }; /** * The refetchDelay terminated normally. */ "localQueue:getJobs:complete": { ctx: WorkerPluginContext; localQueue: LocalQueue; jobs: Job[]; }; /** * The refetchDelay terminated normally. */ "localQueue:returnJobs": { ctx: WorkerPluginContext; localQueue: LocalQueue; jobs: Job[]; }; /** * When a worker is created */ "worker:create": { ctx: WorkerPluginContext; worker: Worker; tasks: TaskList; }; /** * When a worker release is requested */ "worker:release": { ctx: WorkerPluginContext; worker: Worker; }; /** * When a worker stops (normally after a release) */ "worker:stop": { ctx: WorkerPluginContext; worker: Worker; error?: unknown; }; /** * When a worker is about to ask the database for a job to execute */ "worker:getJob:start": { ctx: WorkerPluginContext; worker: Worker; }; /** * When a worker calls get_job but there are no available jobs */ "worker:getJob:error": { ctx: WorkerPluginContext; worker: Worker; error: unknown; }; /** * When a worker calls get_job but there are no available jobs */ "worker:getJob:empty": { ctx: WorkerPluginContext; worker: Worker; }; /** * When an unexpected error occurs outside of task execution itself - for * example when trying to update a task's status in the database after it * completed. */ "worker:fatalError": { ctx: WorkerPluginContext; worker: Worker; error: unknown; jobError: unknown | null; }; /** * When a job is retrieved by get_job */ "job:start": { ctx: WorkerPluginContext; worker: Worker; job: Job; }; /** * When a job completes successfully */ "job:success": { ctx: WorkerPluginContext; worker: Worker; job: Job; }; /** * When a job throws an error */ "job:error": { ctx: WorkerPluginContext; worker: Worker; job: Job; error: unknown; batchJobErrors?: unknown[]; }; /** * When a job fails permanently (emitted after job:error when appropriate) */ "job:failed": { ctx: WorkerPluginContext; worker: Worker; job: Job; error: unknown; batchJobErrors?: unknown[]; }; /** * When a job has finished executing and the result (success or failure) has * been written back to the database */ "job:complete": { ctx: WorkerPluginContext; worker: Worker; job: Job; error: unknown; }; /** **Experimental** When the cron starts working (before backfilling) */ "cron:starting": { ctx: WorkerPluginContext; cron: Cron; start: Date; }; /** **Experimental** When the cron starts working (after backfilling completes) */ "cron:started": { ctx: WorkerPluginContext; cron: Cron; start: Date; }; /** **Experimental** When a number of jobs need backfilling for a particular timestamp. */ "cron:backfill": { ctx: WorkerPluginContext; cron: Cron; itemsToBackfill: JobAndCronIdentifierWithDetails[]; timestamp: string; }; /** * **Experimental** When it seems that time went backwards (e.g. the system * clock was adjusted) and we try again a little later. */ "cron:prematureTimer": { ctx: WorkerPluginContext; cron: Cron; currentTimestamp: number; expectedTimestamp: number; }; /** * **Experimental** When it seems that time jumped forwards (e.g. the system * was overloaded and couldn't fire the timer on time, or perhaps the system * went to sleep) and we need to catch up. */ "cron:overdueTimer": { ctx: WorkerPluginContext; cron: Cron; currentTimestamp: number; expectedTimestamp: number; }; /** * **Experimental** When 1 or more cron items match the current timestamp and * will be scheduled into the database. (Like cron:scheduled but before the * database write.) */ "cron:schedule": { ctx: WorkerPluginContext; cron: Cron; timestamp: number; jobsAndIdentifiers: JobAndCronIdentifier[]; }; /** * **Experimental** When 1 or more cron items match the current timestamp and * were scheduled into the database. (Like cron:schedule but after the * database write.) */ "cron:scheduled": { ctx: WorkerPluginContext; cron: Cron; timestamp: number; jobsAndIdentifiers: JobAndCronIdentifier[]; }; /** * **Experimental** When we trigger the 'resetLocked' cleanup process * (currently every 8-10 minutes) */ "resetLocked:started": { ctx: WorkerPluginContext; /** @internal Not sure this'll stay on pool */ workerPool: WorkerPool; }; /** * **Experimental** When the `resetLocked` process has completed * successfully. */ "resetLocked:success": { ctx: WorkerPluginContext; /** * The number of milliseconds until resetLocked runs again (or null if we * won't because the pool is exiting) */ delay: number | null; /** @internal Not sure this'll stay on pool */ workerPool: WorkerPool; }; /** * **Experimental** When the `resetLocked` process has failed. */ "resetLocked:failure": { ctx: WorkerPluginContext; error: Error; /** * The number of milliseconds until resetLocked runs again (or null if we * won't because the pool is exiting) */ delay: number | null; /** @internal Not sure this'll stay on pool */ workerPool: WorkerPool; }; /** * When the runner is terminated by a signal */ gracefulShutdown: { ctx: WorkerPluginContext; signal: Signal; }; /** * When the runner is terminated by a signal _again_ after 5 seconds */ forcefulShutdown: { ctx: WorkerPluginContext; signal: Signal; }; /** * When the runner is stopped */ stop: { ctx: WorkerPluginContext; }; }; export type WorkerEvents = TypedEventEmitter; export type GlobalEventMap = { /** * When the runner is terminated by a signal */ gracefulShutdown: { signal: Signal; }; /** * When the runner is terminated by a signal _again_ after 5 seconds */ forcefulShutdown: { signal: Signal; }; }; export type GlobalEvents = TypedEventEmitter; /** * The digest of a timestamp into the component parts that a cron schedule cares about. */ export interface TimestampDigest { min: number; hour: number; date: number; month: number; dow: number; } /** Details of a file (guaranteed not to be a directory, nor a symlink) */ export interface FileDetails { /** The full path to the file (possibly relative to the current working directory) */ fullPath: string; /** The stats of the file */ stats: Stats; /** The name of the file, excluding any extensions */ baseName: string; /** The extensions of the file, e.g. `""` for no extensions, `".js"` or even `".test.js"`. */ extension: string; } export type Writeable = { -readonly [P in keyof T]: T[P]; }; export interface WorkerPluginBaseContext { version: string; resolvedPreset: ResolvedWorkerPreset; workerSchema: string; escapedWorkerSchema: string; events: WorkerEvents; logger: Logger; } export interface WorkerPluginContext extends WorkerPluginBaseContext { hooks: AsyncHooks; middleware: Middleware; maxMigrationNumber: number; breakingMigrationNumbers: number[]; } export type GetJobFunction = (workerId: string, flagsToSkip: string[] | null) => PromiseOrDirect; export type CompleteJobFunction = (job: DbJob) => void; export type FailJobFunction = (spec: { job: DbJob; message: string; replacementPayload: undefined | unknown[]; }) => void; export declare const LocalQueueModes: { readonly STARTING: "STARTING"; readonly POLLING: "POLLING"; readonly WAITING: "WAITING"; readonly TTL_EXPIRED: "TTL_EXPIRED"; readonly RELEASED: "RELEASED"; }; export type LocalQueueMode = keyof typeof LocalQueueModes; export {};