///
import { CompiledSharedOptions, EnhancedWithPgClient, LocalQueueMode, WorkerPoolOptions } from ".";
import { Deferred } from "./deferred";
import { GetJobFunction, Job, TaskList, WorkerPool } from "./interfaces";
/**
* The local queue exists to reduce strain on the database; it works by
* fetching a batch of jobs from the database and distributing them to workers
* as and when necessary. It is also responsible for polling when in use,
* relieving the workers of this responsibility.
*
* The local queue trades latency for throughput: jobs may sit in the local
* queue for a longer time (maximum `localQueue.size` jobs waiting maximum
* `localQueue.ttl` milliseconds), but fewer requests to the database are made
* for jobs since more jobs are fetched at once, enabling the worker to reach
* higher levels of performance (and reducing read stress on the DB).
*
* The local queue is always in one of these modes:
*
* - STARTING mode
* - POLLING mode
* - WAITING mode
* - TTL_EXPIRED mode
* - RELEASED mode
*
* ## STARTING mode
*
* STARTING mode is the initial state of the local queue.
*
* Immediately move to POLLING mode.
*
* ## POLLING mode
*
* The queue will only be in POLLING mode when it contains no cached jobs.
*
* When the queue enters POLLING mode:
*
* - if any refetch delay has expired it will trigger a fetch of jobs from the
* database,
* - otherwise it will trigger a refetch to happen once the refetch delay has
* completed.
*
* When jobs are fetched:
*
* - if fewer than `Math.ceil(Math.min(localQueueRefetchDelay.threshold, localQueueSize))`
* jobs were returned then a refetch delay will be set (if configured).
* - if jobs were returned then it will supply as many as possible to any
* waiting workers (`workerQueue`)
* - if all workers are busy and jobs still remain it will store them to
* `jobQueue` and immediately enter WAITING mode
* - otherwise (if no jobs remain: `jobQueue` is empty) we'll wait
* `pollInterval` ms and then fetch again.
*
* When a "new job" notification is received, once any required refetch delay
* has expired (or immediately if it has already expired) the timer will be
* cancelled, and a fetch will be fired immediately.
*
* ## WAITING mode
*
* The local queue can only be in WAITING mode if there are cached jobs.
*
* Any waiting clients are issued any available cached jobs.
*
* If no cached jobs remain, then the local queue enters POLLING mode,
* triggering a fetch.
*
* If cached jobs remain (even if there's just one, even if it has been 30
* minutes since the last fetch) then the local queue continues to wait for
* a worker to claim the remaining jobs. Once no jobs remain, the local queue
* reverts to POLLING mode, triggering a fetch.
*
* In WAITING mode, all "new job" announcements are ignored.
*
* The local queue can be in WAITING mode for at most `getJobBatchTime`
* milliseconds (default: 30 minutes), after which all unclaimed jobs are
* returned to the pool and the local queue enters TTL_EXPIRED mode.
*
* ## TTL_EXPIRED mode
*
* This mode is used when jobs were queued in WAITING mode for too long. The
* local queue will sit in TTL_EXPIRED mode until a worker asks for a job,
* whereupon the local queue will enter POLLING mode (triggering a fetch).
*
* ## RELEASED mode
*
* Triggered on shutdown.
*/
export declare class LocalQueue {
private readonly ctx;
private readonly tasks;
private readonly withPgClient;
readonly workerPool: WorkerPool;
/** How many jobs to fetch at once */
private readonly getJobBatchSize;
/**
* If false, exit once the DB seems to have been exhausted of jobs, even if
* for just a moment. (I.e. `runOnce()`)
*/
private readonly continuous;
private readonly onMajorError;
/**
* The configured time (in milliseconds) that a job may sit unclaimed in the
* local queue before being returned to the database.
*/
readonly ttl: number;
/**
* The time interval (in milliseconds) between fetch requests when in
* `POLLING` mode.
*/
readonly pollInterval: number;
/**
* The jobs that have been pulled from the database that are waiting for a
* worker to claim them. Once claimed, a job will be removed from this list.
* This should be empty in POLLING and TTL_EXPIRED modes.
*/
readonly jobQueue: Job[];
/**
* Workers waiting for jobs are represented by deferred promises in this
* list. When a job becomes available, first it attempts to satisfy one of
* these from the workerQueue, and only if this is empty does it then add the
* job to the `jobQueue`.
*/
readonly workerQueue: Deferred[];
/**
* Are we currently fetching jobs from the DB? Prevents double-fetches.
*/
fetchInProgress: boolean;
/**
* When we enter WAITING mode (i.e. there are jobs in `jobQueue`), we set up
* this timer. When the timer fires, we will release any remaining jobs in
* jobQueue back to the database (and enter TTL_EXPIRED mode). Note: all jobs
* are fetched at once, and no further jobs are fetched, so the TTL for all
* jobs will expire at the same time - we'll only return to POLLING mode once
* all jobs have been executed.
*/
ttlExpiredTimer: NodeJS.Timeout | null;
/**
* The timer associated with the next fetch poll (see also `pollInterval`).
*/
fetchTimer: NodeJS.Timeout | null;
/**
* Should we fetch again once the current fetch is complete? This is
* generally used to indicate that we received a "new job" notification (the
* queue is "pulsed") whilst we were already fetching, so our fetch may not
* have included that job.
*/
fetchAgain: boolean;
/**
* The mode that the queue is in; must only be changed via `setMode`, which
* itself must only be called by the `setMode*()` methods.
*/
readonly mode: LocalQueueMode;
/**
* The promise that resolves/rejects when the local queue has been released.
* Will not resolve until all locally queued jobs have been returned to the
* pool (or may reject if this process fails) and all active fetches and
* other background tasks are complete. This is important, otherwise we might
* release the pg.Pool that we're using before jobs are returned to the
* database, which would be something we couldn't recover from!
*
* If it rejects, may reject with a regular Error or an AggregateError
* representing multiple failures.
*/
private _finPromise;
/**
* Errors that occurred causing the shutdown or during the shutdown of this
* local queue instance.
*/
private errors;
/**
* A count of the number of "background" processes such as fetching or
* returning jobs such that we can avoid exiting until all background tasks
* have completed.
*/
private backgroundCount;
/**
* If `localQueueRefetchDelay` is configured; set this true if the fetch
* resulted in a queue size lower than the threshold.
*/
private refetchDelayActive;
/**
* If true, when the refetch delay expires in POLLING mode (or when we next
* enter POLLING mode after it expires), immediately trigger a fetch. If
* false, just wait for the regular POLLING timeouts.
*/
private refetchDelayFetchOnComplete;
/** The timer tracking when the refetch delay has expired. */
private refetchDelayTimer;
/**
* The number of new jobs received during the fetch or the resulting refetch
* delay; see also `refetchDelayAbortThreshold`.
*/
private refetchDelayCounter;
/**
* A random number between 0 and either
* `preset.worker.localQueue.refetchDelay.maxAbortThreshold` or
* `5*preset.worker.localQueue.size`; when we've been informed of this many
* jobs via pulse(), we must abort the refetch delay and trigger an immediate
* fetch.
*/
private refetchDelayAbortThreshold;
constructor(ctx: CompiledSharedOptions, tasks: TaskList, withPgClient: EnhancedWithPgClient, workerPool: WorkerPool,
/** How many jobs to fetch at once */
getJobBatchSize: number,
/**
* If false, exit once the DB seems to have been exhausted of jobs, even if
* for just a moment. (I.e. `runOnce()`)
*/
continuous: boolean, onMajorError: (e: unknown) => void);
/**
* Only call this from `setMode*()` helpers.
*/
private setMode;
/**
* Called when the LocalQueue is completely finished and released: no
* background tasks, no jobs in job queue. Resolves (or rejects)
* `_finPromise`.
*/
private fin;
private decreaseBackgroundCount;
private decreaseBackgroundCountWithError;
/**
* Track promises that happen in the background, but that we want to ensure are
* handled before we release the queue (so that the database pool isn't
* released too early).
*
* IMPORTANT: never raise an error from background unless mode === "RELEASED" - you
* need to handle errors yourself!
*/
private background;
private setModePolling;
private setModeWaiting;
private setModeTtlExpired;
private returnJobs;
private receivedJobs;
private fetch;
private _fetch;
private refetchDelayCompleteOrAbort;
/**
* If no refetch delay is active, returns false; otherwise returns true and
* checks to see if we need to abort the delay and trigger a fetch.
*/
private handleCheckRefetchDelayAbortThreshold;
/** Called when a new job becomes available in the DB */
pulse(count: number): void;
getJob: GetJobFunction;
release(): Deferred;
private setModeReleased;
}