bunqueue-client 0.1.5 → 0.1.6
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/CHANGELOG.md +77 -0
- package/README.md +43 -0
- package/dist/ack-batcher.d.ts +28 -0
- package/dist/ack-batcher.js +67 -0
- package/dist/backpressure.d.ts +22 -0
- package/dist/backpressure.js +39 -0
- package/dist/connection-pool.d.ts +30 -0
- package/dist/connection-pool.js +60 -0
- package/dist/connection-types.d.ts +21 -1
- package/dist/connection.d.ts +12 -3
- package/dist/connection.js +39 -3
- package/dist/flow-types.d.ts +2 -1
- package/dist/flow.js +12 -3
- package/dist/index.d.ts +7 -2
- package/dist/index.js +3 -1
- package/dist/job.d.ts +2 -2
- package/dist/observability.d.ts +95 -0
- package/dist/observability.js +110 -0
- package/dist/queue-control.js +3 -4
- package/dist/queue-query.js +8 -18
- package/dist/queue.d.ts +13 -4
- package/dist/queue.js +14 -7
- package/dist/responses.d.ts +79 -0
- package/dist/responses.js +10 -0
- package/dist/worker-base.d.ts +2 -0
- package/dist/worker-base.js +5 -0
- package/dist/worker-types.d.ts +15 -1
- package/dist/worker.d.ts +4 -0
- package/dist/worker.js +43 -6
- package/package.json +2 -1
- package/src/ack-batcher.ts +76 -0
- package/src/backpressure.ts +40 -0
- package/src/connection-pool.ts +71 -0
- package/src/connection-types.ts +23 -1
- package/src/connection.ts +42 -6
- package/src/flow-types.ts +2 -1
- package/src/flow.ts +24 -16
- package/src/index.ts +28 -2
- package/src/job.ts +3 -3
- package/src/observability.ts +158 -0
- package/src/queue-control.ts +6 -6
- package/src/queue-query.ts +35 -27
- package/src/queue.ts +30 -11
- package/src/responses.ts +96 -0
- package/src/worker-base.ts +6 -0
- package/src/worker-types.ts +16 -1
- package/src/worker.ts +45 -6
package/dist/index.d.ts
CHANGED
|
@@ -7,16 +7,21 @@ export type { DlqFilter, DlqStats } from './bunqueue/dlq-rate-limit.js';
|
|
|
7
7
|
export type { BatchConfig, BatchProcessor, BunqueueConnection, BunqueueDebounceConfig, BunqueueDeduplicationConfig, BunqueueDlqConfig, BunqueueMiddleware, BunqueueOptions, CircuitBreakerConfig, CircuitState, JobTtlConfig, PriorityAgingConfig, RateLimiterOptions, RetryConfig, RetryStrategy, TriggerRule, } from './bunqueue/types.js';
|
|
8
8
|
export type { Command, ConnectionOptions, Response, TlsOption } from './connection.js';
|
|
9
9
|
export { Connection } from './connection.js';
|
|
10
|
+
export { ConnectionPool } from './connection-pool.js';
|
|
11
|
+
export type { ConnectionLike } from './connection-types.js';
|
|
10
12
|
export { AuthError, BunqueueError, CommandError, CommandTimeoutError, ConnectionClosedError, UnrecoverableError, } from './errors.js';
|
|
11
13
|
export { FlowProducer } from './flow.js';
|
|
12
14
|
export type { FlowJob, FlowProducerOptions, FlowStep, GetFlowOptions, JobNode, } from './flow-types.js';
|
|
13
15
|
export { MAX_FRAME_SIZE, PROTOCOL_VERSION } from './frame.js';
|
|
14
16
|
export type { JobRaw } from './job.js';
|
|
15
17
|
export { Job } from './job.js';
|
|
18
|
+
export type { Logger, LogLevel, Observability, TelemetryEvent, TelemetryHandler, } from './observability.js';
|
|
19
|
+
export { consoleLogger, noopLogger } from './observability.js';
|
|
16
20
|
export type { BulkJobEntry, QueueOptions } from './queue.js';
|
|
17
21
|
export { Queue } from './queue.js';
|
|
18
22
|
export type { SchedulerOptions } from './queue-admin.js';
|
|
23
|
+
export type { BatchResponse, CountResponse, DataResponse, JobCountsResponse, JobResponse, JobsResponse, OkResponse, PausedResponse, ProgressResponse, PulledJobResponse, PulledJobsResponse, ResultResponse, StateResponse, WaitJobResponse, } from './responses.js';
|
|
19
24
|
export type { BackoffOptions, DeduplicationOptions, JobCounts, JobOptions, JobStateName, RepeatOptions, } from './types.js';
|
|
20
25
|
export { Worker } from './worker.js';
|
|
21
|
-
export type { Processor, WorkerOptions } from './worker-types.js';
|
|
22
|
-
export declare const __version__ = "0.1.
|
|
26
|
+
export type { AckBatchOptions, Processor, WorkerOptions } from './worker-types.js';
|
|
27
|
+
export declare const __version__ = "0.1.6";
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
// Simple Mode (Bunqueue): all-in-one Queue + Worker, 1:1 with the official client
|
|
6
6
|
export { Bunqueue } from './bunqueue/bunqueue.js';
|
|
7
7
|
export { Connection } from './connection.js';
|
|
8
|
+
export { ConnectionPool } from './connection-pool.js';
|
|
8
9
|
export { AuthError, BunqueueError, CommandError, CommandTimeoutError, ConnectionClosedError, UnrecoverableError, } from './errors.js';
|
|
9
10
|
export { FlowProducer } from './flow.js';
|
|
10
11
|
export { MAX_FRAME_SIZE, PROTOCOL_VERSION } from './frame.js';
|
|
11
12
|
export { Job } from './job.js';
|
|
13
|
+
export { consoleLogger, noopLogger } from './observability.js';
|
|
12
14
|
export { Queue } from './queue.js';
|
|
13
15
|
export { Worker } from './worker.js';
|
|
14
|
-
export const __version__ = '0.1.
|
|
16
|
+
export const __version__ = '0.1.6';
|
package/dist/job.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Job wrapper: read view over the server job record + per-job operations.
|
|
3
3
|
* Mirrors the official TS client's Job surface (TCP mode).
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { ConnectionLike } from './connection-types.js';
|
|
6
6
|
export type JobRaw = Record<string, unknown>;
|
|
7
7
|
export type ProgressHook = (job: Job, progress: number) => void;
|
|
8
8
|
export declare class Job<T = unknown> {
|
|
@@ -10,7 +10,7 @@ export declare class Job<T = unknown> {
|
|
|
10
10
|
readonly token: string | undefined;
|
|
11
11
|
private readonly conn;
|
|
12
12
|
private readonly onProgress;
|
|
13
|
-
constructor(raw: JobRaw, connection?:
|
|
13
|
+
constructor(raw: JobRaw, connection?: ConnectionLike, token?: string, onProgress?: ProgressHook);
|
|
14
14
|
get id(): string;
|
|
15
15
|
get queue(): string;
|
|
16
16
|
get data(): T;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability primitives: a pluggable structured logger and a telemetry
|
|
3
|
+
* sink. Zero hard dependencies — wire your own OpenTelemetry / Prometheus /
|
|
4
|
+
* logging stack through these interfaces. Defaults are no-ops, so the SDK is
|
|
5
|
+
* silent unless you opt in.
|
|
6
|
+
*/
|
|
7
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
8
|
+
export interface Logger {
|
|
9
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
10
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
11
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
12
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
13
|
+
}
|
|
14
|
+
/** Silent logger (the default). */
|
|
15
|
+
export declare const noopLogger: Logger;
|
|
16
|
+
/**
|
|
17
|
+
* A console-backed logger. Levels below `min` are dropped. Opt-in — pass it as
|
|
18
|
+
* `logger` when you want the SDK to log to the console.
|
|
19
|
+
*/
|
|
20
|
+
export declare function consoleLogger(min?: LogLevel): Logger;
|
|
21
|
+
/** A single telemetry data point. A discriminated union keyed on `type`. */
|
|
22
|
+
export type TelemetryEvent = {
|
|
23
|
+
type: 'command';
|
|
24
|
+
cmd: string;
|
|
25
|
+
reqId: string;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
ok: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'command_timeout';
|
|
30
|
+
cmd: string;
|
|
31
|
+
reqId: string;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'connect';
|
|
34
|
+
host: string;
|
|
35
|
+
port: number;
|
|
36
|
+
generation: number;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
} | {
|
|
39
|
+
type: 'disconnect';
|
|
40
|
+
host: string;
|
|
41
|
+
port: number;
|
|
42
|
+
generation: number;
|
|
43
|
+
} | {
|
|
44
|
+
type: 'reconnect_scheduled';
|
|
45
|
+
host: string;
|
|
46
|
+
port: number;
|
|
47
|
+
attempt: number;
|
|
48
|
+
delayMs: number;
|
|
49
|
+
} | {
|
|
50
|
+
type: 'auth';
|
|
51
|
+
ok: boolean;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'backpressure';
|
|
54
|
+
inFlight: number;
|
|
55
|
+
maxInFlight: number;
|
|
56
|
+
};
|
|
57
|
+
export type TelemetryHandler = (event: TelemetryEvent) => void;
|
|
58
|
+
/** Lifecycle telemetry types that are ALSO emitted as EventEmitter events. */
|
|
59
|
+
export declare const LIFECYCLE_EVENTS: readonly ["connect", "disconnect", "reconnect_scheduled"];
|
|
60
|
+
export interface Observability {
|
|
61
|
+
/** Structured logger; defaults to {@link noopLogger}. */
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
/**
|
|
64
|
+
* Telemetry sink for metrics and tracing. Receives every command's latency,
|
|
65
|
+
* connect/disconnect/reconnect, auth and backpressure events. Bridge it to
|
|
66
|
+
* OpenTelemetry spans or Prometheus counters without the SDK depending on
|
|
67
|
+
* either.
|
|
68
|
+
*/
|
|
69
|
+
onTelemetry?: TelemetryHandler;
|
|
70
|
+
}
|
|
71
|
+
/** High-resolution monotonic clock (Node/Bun/Deno all expose `performance`). */
|
|
72
|
+
export declare const nowMs: () => number;
|
|
73
|
+
/**
|
|
74
|
+
* Fans a telemetry event out to (1) the telemetry sink, (2) the logger, and
|
|
75
|
+
* (3) — for lifecycle events — the owning EventEmitter, so users can both
|
|
76
|
+
* scrape metrics and attach imperative `connection.on('connect', …)` handlers.
|
|
77
|
+
*/
|
|
78
|
+
export declare class Telemetry {
|
|
79
|
+
private readonly logger;
|
|
80
|
+
private readonly sink;
|
|
81
|
+
private readonly emit;
|
|
82
|
+
constructor(obs: Observability | undefined, emit: (event: string, payload: unknown) => void);
|
|
83
|
+
/** Observability must never break the client: swallow consumer errors. */
|
|
84
|
+
private static safely;
|
|
85
|
+
get log(): Logger;
|
|
86
|
+
/** Record a completed command's latency and outcome. */
|
|
87
|
+
command(cmd: string, reqId: string, startMs: number, ok: boolean): void;
|
|
88
|
+
timeout(cmd: string, reqId: string): void;
|
|
89
|
+
connected(host: string, port: number, generation: number, startMs: number): void;
|
|
90
|
+
disconnected(host: string, port: number, generation: number): void;
|
|
91
|
+
reconnectScheduled(host: string, port: number, attempt: number, delayMs: number): void;
|
|
92
|
+
auth(ok: boolean): void;
|
|
93
|
+
backpressure(inFlight: number, maxInFlight: number): void;
|
|
94
|
+
private dispatch;
|
|
95
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability primitives: a pluggable structured logger and a telemetry
|
|
3
|
+
* sink. Zero hard dependencies — wire your own OpenTelemetry / Prometheus /
|
|
4
|
+
* logging stack through these interfaces. Defaults are no-ops, so the SDK is
|
|
5
|
+
* silent unless you opt in.
|
|
6
|
+
*/
|
|
7
|
+
/** Silent logger (the default). */
|
|
8
|
+
export const noopLogger = {
|
|
9
|
+
debug() { },
|
|
10
|
+
info() { },
|
|
11
|
+
warn() { },
|
|
12
|
+
error() { },
|
|
13
|
+
};
|
|
14
|
+
const LEVELS = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
15
|
+
/**
|
|
16
|
+
* A console-backed logger. Levels below `min` are dropped. Opt-in — pass it as
|
|
17
|
+
* `logger` when you want the SDK to log to the console.
|
|
18
|
+
*/
|
|
19
|
+
export function consoleLogger(min = 'info') {
|
|
20
|
+
const floor = LEVELS[min];
|
|
21
|
+
const line = (lvl, msg, meta) => {
|
|
22
|
+
if (LEVELS[lvl] < floor)
|
|
23
|
+
return;
|
|
24
|
+
const suffix = meta && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : '';
|
|
25
|
+
const sink = lvl === 'debug' ? console.log : console[lvl];
|
|
26
|
+
sink(`[bunqueue] ${lvl} ${msg}${suffix}`);
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
debug: (m, meta) => line('debug', m, meta),
|
|
30
|
+
info: (m, meta) => line('info', m, meta),
|
|
31
|
+
warn: (m, meta) => line('warn', m, meta),
|
|
32
|
+
error: (m, meta) => line('error', m, meta),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Lifecycle telemetry types that are ALSO emitted as EventEmitter events. */
|
|
36
|
+
export const LIFECYCLE_EVENTS = ['connect', 'disconnect', 'reconnect_scheduled'];
|
|
37
|
+
/** High-resolution monotonic clock (Node/Bun/Deno all expose `performance`). */
|
|
38
|
+
export const nowMs = () => performance.now();
|
|
39
|
+
/**
|
|
40
|
+
* Fans a telemetry event out to (1) the telemetry sink, (2) the logger, and
|
|
41
|
+
* (3) — for lifecycle events — the owning EventEmitter, so users can both
|
|
42
|
+
* scrape metrics and attach imperative `connection.on('connect', …)` handlers.
|
|
43
|
+
*/
|
|
44
|
+
export class Telemetry {
|
|
45
|
+
logger;
|
|
46
|
+
sink;
|
|
47
|
+
emit;
|
|
48
|
+
constructor(obs, emit) {
|
|
49
|
+
// Wrap the user logger so a throwing logger can never break the transport
|
|
50
|
+
// hot path (dispatch runs inside socket 'data' handlers and connect).
|
|
51
|
+
const inner = obs?.logger ?? noopLogger;
|
|
52
|
+
this.logger =
|
|
53
|
+
inner === noopLogger
|
|
54
|
+
? inner
|
|
55
|
+
: {
|
|
56
|
+
debug: (m, meta) => Telemetry.safely(() => inner.debug(m, meta)),
|
|
57
|
+
info: (m, meta) => Telemetry.safely(() => inner.info(m, meta)),
|
|
58
|
+
warn: (m, meta) => Telemetry.safely(() => inner.warn(m, meta)),
|
|
59
|
+
error: (m, meta) => Telemetry.safely(() => inner.error(m, meta)),
|
|
60
|
+
};
|
|
61
|
+
this.sink = obs?.onTelemetry;
|
|
62
|
+
this.emit = emit;
|
|
63
|
+
}
|
|
64
|
+
/** Observability must never break the client: swallow consumer errors. */
|
|
65
|
+
static safely(fn) {
|
|
66
|
+
try {
|
|
67
|
+
fn();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* consumer logging/telemetry error — intentionally ignored */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
get log() {
|
|
74
|
+
return this.logger;
|
|
75
|
+
}
|
|
76
|
+
/** Record a completed command's latency and outcome. */
|
|
77
|
+
command(cmd, reqId, startMs, ok) {
|
|
78
|
+
this.dispatch({ type: 'command', cmd, reqId, durationMs: nowMs() - startMs, ok });
|
|
79
|
+
}
|
|
80
|
+
timeout(cmd, reqId) {
|
|
81
|
+
this.logger.warn('command timed out', { cmd, reqId });
|
|
82
|
+
this.dispatch({ type: 'command_timeout', cmd, reqId });
|
|
83
|
+
}
|
|
84
|
+
connected(host, port, generation, startMs) {
|
|
85
|
+
this.logger.info('connected', { host, port, generation });
|
|
86
|
+
this.dispatch({ type: 'connect', host, port, generation, durationMs: nowMs() - startMs });
|
|
87
|
+
}
|
|
88
|
+
disconnected(host, port, generation) {
|
|
89
|
+
this.logger.info('disconnected', { host, port, generation });
|
|
90
|
+
this.dispatch({ type: 'disconnect', host, port, generation });
|
|
91
|
+
}
|
|
92
|
+
reconnectScheduled(host, port, attempt, delayMs) {
|
|
93
|
+
this.logger.warn('reconnect scheduled', { host, port, attempt, delayMs });
|
|
94
|
+
this.dispatch({ type: 'reconnect_scheduled', host, port, attempt, delayMs });
|
|
95
|
+
}
|
|
96
|
+
auth(ok) {
|
|
97
|
+
this.dispatch({ type: 'auth', ok });
|
|
98
|
+
}
|
|
99
|
+
backpressure(inFlight, maxInFlight) {
|
|
100
|
+
this.dispatch({ type: 'backpressure', inFlight, maxInFlight });
|
|
101
|
+
}
|
|
102
|
+
dispatch(event) {
|
|
103
|
+
// A throwing sink or lifecycle listener must never break the transport:
|
|
104
|
+
// dispatch runs inside socket 'data' handlers and the connect path.
|
|
105
|
+
Telemetry.safely(() => this.sink?.(event));
|
|
106
|
+
if (LIFECYCLE_EVENTS.includes(event.type)) {
|
|
107
|
+
Telemetry.safely(() => this.emit(event.type, event));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
package/dist/queue-control.js
CHANGED
|
@@ -11,8 +11,7 @@ export const controlMethods = {
|
|
|
11
11
|
await this.call({ cmd: 'Resume', queue: this.name });
|
|
12
12
|
},
|
|
13
13
|
async isPaused() {
|
|
14
|
-
|
|
15
|
-
return response.paused === true;
|
|
14
|
+
return (await this.call({ cmd: 'IsPaused', queue: this.name })).paused === true;
|
|
16
15
|
},
|
|
17
16
|
async drain() {
|
|
18
17
|
await this.call({ cmd: 'Drain', queue: this.name });
|
|
@@ -23,7 +22,7 @@ export const controlMethods = {
|
|
|
23
22
|
/** Remove old jobs; returns how many were removed. */
|
|
24
23
|
async clean(graceMs, limit, state) {
|
|
25
24
|
const response = await this.call(compact({ cmd: 'Clean', queue: this.name, grace: graceMs, limit, state }));
|
|
26
|
-
return
|
|
25
|
+
return response.count ?? 0;
|
|
27
26
|
},
|
|
28
27
|
async remove(id) {
|
|
29
28
|
await this.call({ cmd: 'Cancel', id });
|
|
@@ -37,7 +36,7 @@ export const controlMethods = {
|
|
|
37
36
|
/** Promote delayed jobs to waiting; returns how many were promoted. */
|
|
38
37
|
async promoteJobs(opts = {}) {
|
|
39
38
|
const response = await this.call(compact({ cmd: 'PromoteJobs', queue: this.name, count: opts.count }));
|
|
40
|
-
return
|
|
39
|
+
return response.count ?? 0;
|
|
41
40
|
},
|
|
42
41
|
/** BullMQ contract: failed → waiting. */
|
|
43
42
|
async retryJob(id) {
|
package/dist/queue-query.js
CHANGED
|
@@ -13,8 +13,7 @@ export const queryMethods = {
|
|
|
13
13
|
async getJob(id) {
|
|
14
14
|
try {
|
|
15
15
|
const response = await this.call({ cmd: 'GetJob', id });
|
|
16
|
-
|
|
17
|
-
return raw ? new Job(raw, this.connection) : null;
|
|
16
|
+
return response.job ? new Job(response.job, this.connection) : null;
|
|
18
17
|
}
|
|
19
18
|
catch (err) {
|
|
20
19
|
if (err instanceof CommandError)
|
|
@@ -25,8 +24,7 @@ export const queryMethods = {
|
|
|
25
24
|
async getJobByCustomId(customId) {
|
|
26
25
|
try {
|
|
27
26
|
const response = await this.call({ cmd: 'GetJobByCustomId', customId });
|
|
28
|
-
|
|
29
|
-
return raw ? new Job(raw, this.connection) : null;
|
|
27
|
+
return response.job ? new Job(response.job, this.connection) : null;
|
|
30
28
|
}
|
|
31
29
|
catch (err) {
|
|
32
30
|
if (err instanceof CommandError)
|
|
@@ -49,8 +47,7 @@ export const queryMethods = {
|
|
|
49
47
|
offset: start,
|
|
50
48
|
limit: end - start,
|
|
51
49
|
}));
|
|
52
|
-
|
|
53
|
-
return jobs.map((raw) => new Job(raw, this.connection));
|
|
50
|
+
return (response.jobs ?? []).map((raw) => new Job(raw, this.connection));
|
|
54
51
|
},
|
|
55
52
|
getWaiting(start, end) {
|
|
56
53
|
// Only the 'waiting' bucket — prioritized jobs live in a separate bucket
|
|
@@ -76,12 +73,10 @@ export const queryMethods = {
|
|
|
76
73
|
return this.getJobs({ state: 'waiting-children', start, end });
|
|
77
74
|
},
|
|
78
75
|
async getJobState(id) {
|
|
79
|
-
|
|
80
|
-
return String(response.state);
|
|
76
|
+
return (await this.call({ cmd: 'GetState', id })).state;
|
|
81
77
|
},
|
|
82
78
|
async getResult(id) {
|
|
83
|
-
|
|
84
|
-
return response.result;
|
|
79
|
+
return (await this.call({ cmd: 'GetResult', id })).result;
|
|
85
80
|
},
|
|
86
81
|
async getChildrenValues(id) {
|
|
87
82
|
return unwrapValues(await this.call({ cmd: 'GetChildrenValues', id }));
|
|
@@ -130,15 +125,11 @@ export const queryMethods = {
|
|
|
130
125
|
},
|
|
131
126
|
async getProgress(id) {
|
|
132
127
|
const response = await this.call({ cmd: 'GetProgress', id });
|
|
133
|
-
return {
|
|
134
|
-
progress: Number(response.progress ?? 0),
|
|
135
|
-
message: response.message ?? null,
|
|
136
|
-
};
|
|
128
|
+
return { progress: response.progress ?? 0, message: response.message ?? null };
|
|
137
129
|
},
|
|
138
130
|
// ------------------------------------------------------------------- counts
|
|
139
131
|
async getJobCounts() {
|
|
140
|
-
|
|
141
|
-
return response.counts;
|
|
132
|
+
return (await this.call({ cmd: 'GetJobCounts', queue: this.name })).counts;
|
|
142
133
|
},
|
|
143
134
|
async getWaitingCount() {
|
|
144
135
|
// 'waiting' only — prioritized jobs are counted by getPrioritizedCount,
|
|
@@ -164,8 +155,7 @@ export const queryMethods = {
|
|
|
164
155
|
return (await this.getJobCounts())['waiting-children'];
|
|
165
156
|
},
|
|
166
157
|
async count() {
|
|
167
|
-
|
|
168
|
-
return Number(response.count ?? 0);
|
|
158
|
+
return (await this.call({ cmd: 'Count', queue: this.name })).count ?? 0;
|
|
169
159
|
},
|
|
170
160
|
async getCountsPerPriority() {
|
|
171
161
|
const response = await this.call({ cmd: 'GetCountsPerPriority', queue: this.name });
|
package/dist/queue.d.ts
CHANGED
|
@@ -9,18 +9,27 @@
|
|
|
9
9
|
* exposes them on the type.
|
|
10
10
|
*/
|
|
11
11
|
import { Connection, type Response, type TlsOption } from './connection.js';
|
|
12
|
+
import type { ConnectionLike } from './connection-types.js';
|
|
12
13
|
import { Job } from './job.js';
|
|
14
|
+
import type { Observability } from './observability.js';
|
|
13
15
|
import { type QueueAdminApi } from './queue-admin.js';
|
|
14
16
|
import { type QueueControlApi } from './queue-control.js';
|
|
15
17
|
import { type QueueQueryApi } from './queue-query.js';
|
|
16
18
|
import { type JobOptions } from './types.js';
|
|
17
|
-
export interface QueueOptions {
|
|
19
|
+
export interface QueueOptions extends Observability {
|
|
18
20
|
host?: string;
|
|
19
21
|
port?: number;
|
|
20
22
|
token?: string;
|
|
21
23
|
tls?: TlsOption;
|
|
22
24
|
connection?: Connection;
|
|
23
25
|
commandTimeoutMs?: number;
|
|
26
|
+
/** Max in-flight commands before backpressure kicks in (0 = unbounded). */
|
|
27
|
+
maxInFlight?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Fan producer commands across N connections (round-robin) for throughput.
|
|
30
|
+
* >1 builds a ConnectionPool; default 1 = a single connection.
|
|
31
|
+
*/
|
|
32
|
+
poolSize?: number;
|
|
24
33
|
}
|
|
25
34
|
export interface BulkJobEntry<T = unknown> {
|
|
26
35
|
name: string;
|
|
@@ -29,13 +38,13 @@ export interface BulkJobEntry<T = unknown> {
|
|
|
29
38
|
}
|
|
30
39
|
export declare class Queue<T = unknown> {
|
|
31
40
|
readonly name: string;
|
|
32
|
-
readonly connection:
|
|
41
|
+
readonly connection: ConnectionLike;
|
|
33
42
|
private readonly ownsConnection;
|
|
34
43
|
constructor(name: string, opts?: QueueOptions);
|
|
35
44
|
/** Send a raw command on this queue's connection (used by area modules). */
|
|
36
|
-
call(command: Record<string, unknown> & {
|
|
45
|
+
call<R = Response>(command: Record<string, unknown> & {
|
|
37
46
|
cmd: string;
|
|
38
|
-
}, timeoutMs?: number): Promise<
|
|
47
|
+
}, timeoutMs?: number): Promise<R>;
|
|
39
48
|
/** Add a job; returns a Job stub carrying the assigned id. */
|
|
40
49
|
add(name: string, data: T, opts?: JobOptions): Promise<Job<T>>;
|
|
41
50
|
/** Add many jobs in one round-trip; returns Job stubs. */
|
package/dist/queue.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* exposes them on the type.
|
|
10
10
|
*/
|
|
11
11
|
import { Connection } from './connection.js';
|
|
12
|
+
import { ConnectionPool } from './connection-pool.js';
|
|
12
13
|
import { Job } from './job.js';
|
|
13
14
|
import { adminMethods } from './queue-admin.js';
|
|
14
15
|
import { controlMethods } from './queue-control.js';
|
|
@@ -21,15 +22,21 @@ export class Queue {
|
|
|
21
22
|
ownsConnection;
|
|
22
23
|
constructor(name, opts = {}) {
|
|
23
24
|
this.name = name;
|
|
25
|
+
const connOptions = {
|
|
26
|
+
host: opts.host,
|
|
27
|
+
port: opts.port,
|
|
28
|
+
token: opts.token,
|
|
29
|
+
tls: opts.tls,
|
|
30
|
+
commandTimeoutMs: opts.commandTimeoutMs,
|
|
31
|
+
maxInFlight: opts.maxInFlight,
|
|
32
|
+
logger: opts.logger,
|
|
33
|
+
onTelemetry: opts.onTelemetry,
|
|
34
|
+
};
|
|
24
35
|
this.connection =
|
|
25
36
|
opts.connection ??
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
token: opts.token,
|
|
30
|
-
tls: opts.tls,
|
|
31
|
-
commandTimeoutMs: opts.commandTimeoutMs,
|
|
32
|
-
});
|
|
37
|
+
(opts.poolSize && opts.poolSize > 1
|
|
38
|
+
? new ConnectionPool(opts.poolSize, connOptions)
|
|
39
|
+
: new Connection(connOptions));
|
|
33
40
|
this.ownsConnection = opts.connection === undefined;
|
|
34
41
|
}
|
|
35
42
|
/** Send a raw command on this queue's connection (used by area modules). */
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed server-response shapes, mirroring the server's response builders
|
|
3
|
+
* (src/domain/types/response.ts). `Connection.call<R>()` and `Queue.call<R>()`
|
|
4
|
+
* are generic over these, so call sites read `response.job` / `response.counts`
|
|
5
|
+
* with real types instead of casting `as Record<string, unknown>`.
|
|
6
|
+
*
|
|
7
|
+
* These narrow the loose transport `Response`; the runtime value is whatever
|
|
8
|
+
* the server sent, so only assert the shape a given command actually returns.
|
|
9
|
+
*/
|
|
10
|
+
import type { JobRaw } from './job.js';
|
|
11
|
+
import type { JobCounts, JobStateName } from './types.js';
|
|
12
|
+
interface Ok {
|
|
13
|
+
ok: true;
|
|
14
|
+
reqId?: string;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** PUSH → id, or a bare ok ack. */
|
|
18
|
+
export interface OkResponse extends Ok {
|
|
19
|
+
id?: string;
|
|
20
|
+
}
|
|
21
|
+
/** PUSHB → ids. */
|
|
22
|
+
export interface BatchResponse extends Ok {
|
|
23
|
+
ids: string[];
|
|
24
|
+
}
|
|
25
|
+
/** GetJob → job (null when not found is surfaced as a CommandError instead). */
|
|
26
|
+
export interface JobResponse extends Ok {
|
|
27
|
+
job: JobRaw | null;
|
|
28
|
+
}
|
|
29
|
+
/** PULL → job + lock token. */
|
|
30
|
+
export interface PulledJobResponse extends Ok {
|
|
31
|
+
job: JobRaw | null;
|
|
32
|
+
token: string | null;
|
|
33
|
+
}
|
|
34
|
+
/** PULLB → jobs + lock tokens (same order). */
|
|
35
|
+
export interface PulledJobsResponse extends Ok {
|
|
36
|
+
jobs: JobRaw[];
|
|
37
|
+
tokens: string[];
|
|
38
|
+
}
|
|
39
|
+
/** GetJobs → jobs. */
|
|
40
|
+
export interface JobsResponse extends Ok {
|
|
41
|
+
jobs: JobRaw[];
|
|
42
|
+
}
|
|
43
|
+
/** GetState → state. */
|
|
44
|
+
export interface StateResponse extends Ok {
|
|
45
|
+
id: string;
|
|
46
|
+
state: JobStateName;
|
|
47
|
+
}
|
|
48
|
+
/** GetResult → result. */
|
|
49
|
+
export interface ResultResponse<R = unknown> extends Ok {
|
|
50
|
+
result: R;
|
|
51
|
+
}
|
|
52
|
+
/** WaitJob → completed flag + (on completion) result. */
|
|
53
|
+
export interface WaitJobResponse<R = unknown> extends Ok {
|
|
54
|
+
completed: boolean;
|
|
55
|
+
result?: R;
|
|
56
|
+
}
|
|
57
|
+
/** GetJobCounts → counts. */
|
|
58
|
+
export interface JobCountsResponse extends Ok {
|
|
59
|
+
counts: JobCounts;
|
|
60
|
+
}
|
|
61
|
+
/** GetProgress → progress + message. */
|
|
62
|
+
export interface ProgressResponse extends Ok {
|
|
63
|
+
progress: number;
|
|
64
|
+
message: string | null;
|
|
65
|
+
}
|
|
66
|
+
/** IsPaused → paused. */
|
|
67
|
+
export interface PausedResponse extends Ok {
|
|
68
|
+
paused: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Count / Clean / PromoteJobs / RetryDlq → count (+ optional removed ids). */
|
|
71
|
+
export interface CountResponse extends Ok {
|
|
72
|
+
count: number;
|
|
73
|
+
ids?: string[];
|
|
74
|
+
}
|
|
75
|
+
/** Generic data-wrapped payload (logs, workers, values, webhookId, …). */
|
|
76
|
+
export interface DataResponse<T = unknown> extends Ok {
|
|
77
|
+
data: T;
|
|
78
|
+
}
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed server-response shapes, mirroring the server's response builders
|
|
3
|
+
* (src/domain/types/response.ts). `Connection.call<R>()` and `Queue.call<R>()`
|
|
4
|
+
* are generic over these, so call sites read `response.job` / `response.counts`
|
|
5
|
+
* with real types instead of casting `as Record<string, unknown>`.
|
|
6
|
+
*
|
|
7
|
+
* These narrow the loose transport `Response`; the runtime value is whatever
|
|
8
|
+
* the server sent, so only assert the shape a given command actually returns.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/worker-base.d.ts
CHANGED
|
@@ -59,4 +59,6 @@ export declare class WorkerBase extends EventEmitter {
|
|
|
59
59
|
protected safeCall(command: Record<string, unknown> & {
|
|
60
60
|
cmd: string;
|
|
61
61
|
}): Promise<void>;
|
|
62
|
+
/** Hook run during close() before draining in-flight jobs (see Worker). */
|
|
63
|
+
protected beforeClose(): Promise<void>;
|
|
62
64
|
}
|
package/dist/worker-base.js
CHANGED
|
@@ -49,6 +49,8 @@ export class WorkerBase extends EventEmitter {
|
|
|
49
49
|
port: opts.port,
|
|
50
50
|
token: opts.token,
|
|
51
51
|
tls: opts.tls,
|
|
52
|
+
logger: opts.logger,
|
|
53
|
+
onTelemetry: opts.onTelemetry,
|
|
52
54
|
});
|
|
53
55
|
this.readyPromise = new Promise((resolve) => {
|
|
54
56
|
this.readyResolve = resolve;
|
|
@@ -120,6 +122,7 @@ export class WorkerBase extends EventEmitter {
|
|
|
120
122
|
this.stopped = true;
|
|
121
123
|
if (this.loopPromise)
|
|
122
124
|
await this.loopPromise;
|
|
125
|
+
await this.beforeClose(); // flush any batched ACKs before draining
|
|
123
126
|
while (!force && this.active.size > 0)
|
|
124
127
|
await sleep(20);
|
|
125
128
|
if (this.heartbeatTimer) {
|
|
@@ -144,4 +147,6 @@ export class WorkerBase extends EventEmitter {
|
|
|
144
147
|
this.emit('error', err);
|
|
145
148
|
}
|
|
146
149
|
}
|
|
150
|
+
/** Hook run during close() before draining in-flight jobs (see Worker). */
|
|
151
|
+
async beforeClose() { }
|
|
147
152
|
}
|
package/dist/worker-types.d.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
1
|
/** Worker option types and shared constants. */
|
|
2
2
|
import type { TlsOption } from './connection.js';
|
|
3
3
|
import type { Job } from './job.js';
|
|
4
|
+
import type { Observability } from './observability.js';
|
|
4
5
|
export type Processor<T = unknown, R = unknown> = (job: Job<T>) => R | Promise<R>;
|
|
5
|
-
export interface
|
|
6
|
+
export interface AckBatchOptions {
|
|
7
|
+
/** Batch ACKs into ACKB round-trips (default false; opt-in for throughput). */
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
/** Max ACKs per batch (default 50). */
|
|
10
|
+
maxSize?: number;
|
|
11
|
+
/** Max ms to hold a partial batch before flushing (default 5). */
|
|
12
|
+
maxDelayMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface WorkerOptions extends Observability {
|
|
6
15
|
host?: string;
|
|
7
16
|
port?: number;
|
|
8
17
|
token?: string;
|
|
9
18
|
tls?: TlsOption;
|
|
19
|
+
/**
|
|
20
|
+
* Batch completed-job ACKs into ACKB commands for higher throughput under
|
|
21
|
+
* load. Opt-in: the default (individual ACK per job) is unchanged.
|
|
22
|
+
*/
|
|
23
|
+
ackBatch?: AckBatchOptions;
|
|
10
24
|
/** Max jobs processed in parallel (default 4). */
|
|
11
25
|
concurrency?: number;
|
|
12
26
|
/** Max jobs fetched per PULLB (default 10, capped by free slots). */
|
package/dist/worker.d.ts
CHANGED
|
@@ -9,12 +9,16 @@ import { WorkerBase } from './worker-base.js';
|
|
|
9
9
|
import { type Processor, type WorkerOptions } from './worker-types.js';
|
|
10
10
|
export declare class Worker<T = unknown, R = unknown> extends WorkerBase {
|
|
11
11
|
private readonly processor;
|
|
12
|
+
private readonly ackBatcher;
|
|
12
13
|
constructor(queue: string, processor: Processor<T, R>, opts?: WorkerOptions);
|
|
14
|
+
/** Flush batched ACKs before the base class drains in-flight jobs. */
|
|
15
|
+
protected beforeClose(): Promise<void>;
|
|
13
16
|
/** Start the pull loop (no-op if already running). */
|
|
14
17
|
run(): void;
|
|
15
18
|
private loop;
|
|
16
19
|
private pollOnce;
|
|
17
20
|
private runJob;
|
|
21
|
+
private finishJob;
|
|
18
22
|
private startHeartbeat;
|
|
19
23
|
private register;
|
|
20
24
|
}
|