cairnq 0.13.0 → 0.15.0
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 +9 -32
- package/dist/_protocol/sql/postgres/purge.sql +10 -10
- package/dist/_protocol/sql/postgres/stats.sql +3 -2
- package/dist/_protocol/sql/sqlite/purge.sql +10 -10
- package/dist/_protocol/sql/sqlite/stats.sql +3 -2
- package/dist/backpressure.d.ts +0 -3
- package/dist/backpressure.js +1 -3
- package/dist/client.d.ts +42 -29
- package/dist/client.js +29 -22
- package/dist/context.d.ts +23 -1
- package/dist/context.js +18 -7
- package/dist/errors.d.ts +20 -26
- package/dist/errors.js +23 -35
- package/dist/index.d.ts +4 -9
- package/dist/index.js +3 -6
- package/dist/models.d.ts +14 -1
- package/dist/models.js +17 -5
- package/dist/retention.d.ts +13 -2
- package/dist/retention.js +31 -9
- package/dist/store/base.d.ts +35 -77
- package/dist/store/base.js +148 -104
- package/dist/store/pg-executor.d.ts +11 -0
- package/dist/store/pg-executor.js +11 -0
- package/dist/store/pg-pool.js +14 -0
- package/dist/store/postgres.d.ts +18 -12
- package/dist/store/postgres.js +23 -29
- package/dist/store/sqlite.d.ts +39 -2
- package/dist/store/sqlite.js +108 -29
- package/dist/worker.d.ts +38 -93
- package/dist/worker.js +181 -234
- package/package.json +1 -1
- package/src/backpressure.ts +1 -5
- package/src/client.ts +71 -45
- package/src/context.ts +28 -7
- package/src/errors.ts +25 -36
- package/src/index.ts +4 -26
- package/src/models.ts +17 -5
- package/src/retention.ts +30 -9
- package/src/store/base.ts +144 -147
- package/src/store/pg-executor.ts +11 -0
- package/src/store/pg-pool.ts +14 -0
- package/src/store/postgres.ts +24 -31
- package/src/store/sqlite.ts +108 -27
- package/src/worker.ts +208 -300
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
export { CairnQ } from "./client.js";
|
|
2
2
|
export type { CallOptions, ClientOptions, SubmitOptions, WaitOptions } from "./client.js";
|
|
3
|
-
export { QueueDepthGate } from "./backpressure.js";
|
|
4
|
-
export type { BackpressureOptions, QueueDepthLimit } from "./backpressure.js";
|
|
5
|
-
export { RetentionSweeper } from "./retention.js";
|
|
6
3
|
export type { RetentionCutoffs, RetentionOptions, RetentionRule } from "./retention.js";
|
|
7
4
|
export { Worker } from "./worker.js";
|
|
8
5
|
export type { BatchHandler, Handler, TypedHandler, WorkerOptions } from "./worker.js";
|
|
9
6
|
export { TaskContext } from "./context.js";
|
|
10
|
-
export type { TaskContextOptions } from "./context.js";
|
|
11
7
|
export { defineTask } from "./task.js";
|
|
12
8
|
export type { TaskDef } from "./task.js";
|
|
13
9
|
export { SQLiteStore } from "./store/sqlite.js";
|
|
14
10
|
export { PostgresStore } from "./store/postgres.js";
|
|
11
|
+
export { TaskStore } from "./store/base.js";
|
|
12
|
+
export type { Conflict, ListInput, PurgeInput, SubmitInput } from "./store/base.js";
|
|
15
13
|
export { ListenUnavailable } from "./store/pg-executor.js";
|
|
16
14
|
export type { PgExecutor, PgSession, Row } from "./store/pg-executor.js";
|
|
17
15
|
export { createPoolExecutor } from "./store/pg-pool.js";
|
|
18
|
-
export {
|
|
19
|
-
export type { ListInput, PurgeInput, SubmitInput, Conflict, WatchOptions, WatchSignal, } from "./store/base.js";
|
|
20
|
-
export { DEFAULT_WATCH_POLL_MS } from "./store/base.js";
|
|
16
|
+
export { STATUSES, isTerminalStatus } from "./models.js";
|
|
21
17
|
export type { Task, TaskRef, TaskStatus, TerminalStatus } from "./models.js";
|
|
22
|
-
export {
|
|
23
|
-
export { CairnQError, AlreadyExists, QueueFull, TaskTimeout, TaskFailed, TaskCanceled, TaskError, LostLease, EventLoopBlocked, ProtocolVersionMismatch, SchemaMismatch, UnsupportedBackend, SerializationError, } from "./errors.js";
|
|
18
|
+
export { CairnQError, AlreadyExists, QueueFull, TaskTimeout, TaskFailed, TaskCanceled, TaskError, LostLease, ProtocolVersionMismatch, SchemaMismatch, UnsupportedBackend, SerializationError, StoreClosed, } from "./errors.js";
|
|
24
19
|
export type { FailReason } from "./errors.js";
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
export { CairnQ } from "./client.js";
|
|
2
|
-
export { QueueDepthGate } from "./backpressure.js";
|
|
3
|
-
export { RetentionSweeper } from "./retention.js";
|
|
4
2
|
export { Worker } from "./worker.js";
|
|
5
3
|
export { TaskContext } from "./context.js";
|
|
6
4
|
export { defineTask } from "./task.js";
|
|
7
5
|
export { SQLiteStore } from "./store/sqlite.js";
|
|
8
6
|
export { PostgresStore } from "./store/postgres.js";
|
|
7
|
+
export { TaskStore } from "./store/base.js";
|
|
9
8
|
export { ListenUnavailable } from "./store/pg-executor.js";
|
|
10
9
|
export { createPoolExecutor } from "./store/pg-pool.js";
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export { STATUSES, isTerminal, isTerminalStatus, cancelRequested, isQueued, isRunning, isSucceeded, isFailed, isCanceled, } from "./models.js";
|
|
14
|
-
export { CairnQError, AlreadyExists, QueueFull, TaskTimeout, TaskFailed, TaskCanceled, TaskError, LostLease, EventLoopBlocked, ProtocolVersionMismatch, SchemaMismatch, UnsupportedBackend, SerializationError, } from "./errors.js";
|
|
10
|
+
export { STATUSES, isTerminalStatus } from "./models.js";
|
|
11
|
+
export { CairnQError, AlreadyExists, QueueFull, TaskTimeout, TaskFailed, TaskCanceled, TaskError, LostLease, ProtocolVersionMismatch, SchemaMismatch, UnsupportedBackend, SerializationError, StoreClosed, } from "./errors.js";
|
package/dist/models.d.ts
CHANGED
|
@@ -39,7 +39,20 @@ export declare function isTerminalStatus(status: TaskStatus): status is Terminal
|
|
|
39
39
|
/** Map a probe row (see get_status.sql) to a TaskRef — the ref twin of
|
|
40
40
|
* rowToTask, so the row shape stays models' knowledge alone. */
|
|
41
41
|
export declare function rowToRef(row: Record<string, unknown>): TaskRef;
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Map a row to a Task.
|
|
44
|
+
*
|
|
45
|
+
* `jsonIsText` says how this driver delivers a JSON column, and it has to be
|
|
46
|
+
* told rather than inferred. SQLite (TEXT) hands back the JSON text to parse; a
|
|
47
|
+
* jsonb-aware driver (`pg`) hands back an already-decoded value. Those two are
|
|
48
|
+
* distinguishable for an object or an array — but not for a JSON *string*, where
|
|
49
|
+
* the text form (`"hello"`, quotes included) and the decoded form (`hello`) are
|
|
50
|
+
* both, simply, strings. Guessing from the value used to double-parse the
|
|
51
|
+
* decoded one: `"s3://…"` threw a SyntaxError, and `"42"` came back as the
|
|
52
|
+
* NUMBER 42 — silent corruption for any payload or result that is a top-level
|
|
53
|
+
* string. The store knows which driver it has; models does not, so it asks.
|
|
54
|
+
*/
|
|
55
|
+
export declare function rowToTask(row: Record<string, unknown>, jsonIsText: boolean): Task;
|
|
43
56
|
/** Accepts anything carrying a status — a Task or a TaskRef probe. */
|
|
44
57
|
export declare function isTerminal(task: Pick<Task, "status">): boolean;
|
|
45
58
|
export declare function cancelRequested(task: Task): boolean;
|
package/dist/models.js
CHANGED
|
@@ -28,14 +28,26 @@ export function isTerminalStatus(status) {
|
|
|
28
28
|
export function rowToRef(row) {
|
|
29
29
|
return { id: row.id, status: row.status };
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Map a row to a Task.
|
|
33
|
+
*
|
|
34
|
+
* `jsonIsText` says how this driver delivers a JSON column, and it has to be
|
|
35
|
+
* told rather than inferred. SQLite (TEXT) hands back the JSON text to parse; a
|
|
36
|
+
* jsonb-aware driver (`pg`) hands back an already-decoded value. Those two are
|
|
37
|
+
* distinguishable for an object or an array — but not for a JSON *string*, where
|
|
38
|
+
* the text form (`"hello"`, quotes included) and the decoded form (`hello`) are
|
|
39
|
+
* both, simply, strings. Guessing from the value used to double-parse the
|
|
40
|
+
* decoded one: `"s3://…"` threw a SyntaxError, and `"42"` came back as the
|
|
41
|
+
* NUMBER 42 — silent corruption for any payload or result that is a top-level
|
|
42
|
+
* string. The store knows which driver it has; models does not, so it asks.
|
|
43
|
+
*/
|
|
44
|
+
export function rowToTask(row, jsonIsText) {
|
|
32
45
|
const t = { ...row };
|
|
33
46
|
for (const col of JSON_COLUMNS) {
|
|
34
47
|
const v = row[col];
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
t[col] = typeof v === "string" ? JSON.parse(v) : (v ?? null);
|
|
48
|
+
// A SQL NULL and a decoded JSON `null` both arrive as null and both mean
|
|
49
|
+
// "no value"; only a real text form is parsed.
|
|
50
|
+
t[col] = v == null ? null : jsonIsText ? JSON.parse(v) : v;
|
|
39
51
|
}
|
|
40
52
|
for (const col of MS_COLUMNS) {
|
|
41
53
|
const v = row[col];
|
package/dist/retention.d.ts
CHANGED
|
@@ -108,7 +108,18 @@ export declare class RetentionSweeper {
|
|
|
108
108
|
* demand — after a backfill, or from a maintenance command.
|
|
109
109
|
*/
|
|
110
110
|
sweep(): Promise<number>;
|
|
111
|
-
/**
|
|
112
|
-
*
|
|
111
|
+
/**
|
|
112
|
+
* Sleep, interruptible by stop().
|
|
113
|
+
*
|
|
114
|
+
* `holdProcess` is the same distinction the store draws between `claimWake`
|
|
115
|
+
* and `taskDoneWake`: a wait nobody is awaiting must not hold the process
|
|
116
|
+
* open, and a wait somebody IS awaiting must. The scheduled loop's interval is
|
|
117
|
+
* the first — retention is housekeeping, and a pending sweep must never be the
|
|
118
|
+
* reason a process refuses to exit. `sweep()`'s between-batches yield is the
|
|
119
|
+
* second: its caller is awaiting the drain, and an unref'd timer there let
|
|
120
|
+
* Node decide the loop was idle and exit mid-drain, leaving that promise
|
|
121
|
+
* unsettled forever (a maintenance command that swept two rows of seven and
|
|
122
|
+
* exited 13). Python's twin never had this, `asyncio.sleep` having no unref.
|
|
123
|
+
*/
|
|
113
124
|
private sleep;
|
|
114
125
|
}
|
package/dist/retention.js
CHANGED
|
@@ -86,9 +86,10 @@ export class RetentionSweeper {
|
|
|
86
86
|
if (this.active)
|
|
87
87
|
return;
|
|
88
88
|
this.active = true;
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
this.
|
|
89
|
+
// No reset here: stop() clears `stopping` and re-arms the signal before it
|
|
90
|
+
// returns, and the constructor arms the first one — so a sweeper reaching
|
|
91
|
+
// this line always already has a fresh signal and a clear flag. (The Python
|
|
92
|
+
// twin has never had a reset here, for the same reason.)
|
|
92
93
|
this.loop = this.run();
|
|
93
94
|
}
|
|
94
95
|
/** Mint a fresh stop signal. */
|
|
@@ -102,6 +103,15 @@ export class RetentionSweeper {
|
|
|
102
103
|
this.wake();
|
|
103
104
|
await this.loop;
|
|
104
105
|
this.loop = null;
|
|
106
|
+
// Cleared, and the signal re-armed, now that the loop is provably gone.
|
|
107
|
+
// `stopping` is how a sweep in flight cuts itself short, so leaving it set
|
|
108
|
+
// would silently truncate a later on-demand sweep() — a supported call — to
|
|
109
|
+
// its first batch. Leaving the signal spent would be the subtler half of the
|
|
110
|
+
// same bug: that sweep's between-batches yield would resolve on a microtask
|
|
111
|
+
// instead of handing the event loop back, so a long drain would starve the
|
|
112
|
+
// submits and claims sharing this process — exactly what the yield is for.
|
|
113
|
+
this.stopping = false;
|
|
114
|
+
this.arm();
|
|
105
115
|
}
|
|
106
116
|
async run() {
|
|
107
117
|
// Sleep first: a process that restarts often would otherwise purge on every
|
|
@@ -140,19 +150,31 @@ export class RetentionSweeper {
|
|
|
140
150
|
if (ids.length < this.limit)
|
|
141
151
|
break;
|
|
142
152
|
// Hand the loop back between batches: a large drain must not starve the
|
|
143
|
-
// submits and claims sharing this process.
|
|
144
|
-
await this.sleep(0);
|
|
153
|
+
// submits and claims sharing this process. Ref'd — see sleep().
|
|
154
|
+
await this.sleep(0, true);
|
|
145
155
|
}
|
|
146
156
|
}
|
|
147
157
|
return deleted;
|
|
148
158
|
}
|
|
149
|
-
/**
|
|
150
|
-
*
|
|
151
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Sleep, interruptible by stop().
|
|
161
|
+
*
|
|
162
|
+
* `holdProcess` is the same distinction the store draws between `claimWake`
|
|
163
|
+
* and `taskDoneWake`: a wait nobody is awaiting must not hold the process
|
|
164
|
+
* open, and a wait somebody IS awaiting must. The scheduled loop's interval is
|
|
165
|
+
* the first — retention is housekeeping, and a pending sweep must never be the
|
|
166
|
+
* reason a process refuses to exit. `sweep()`'s between-batches yield is the
|
|
167
|
+
* second: its caller is awaiting the drain, and an unref'd timer there let
|
|
168
|
+
* Node decide the loop was idle and exit mid-drain, leaving that promise
|
|
169
|
+
* unsettled forever (a maintenance command that swept two rows of seven and
|
|
170
|
+
* exited 13). Python's twin never had this, `asyncio.sleep` having no unref.
|
|
171
|
+
*/
|
|
172
|
+
sleep(ms, holdProcess = false) {
|
|
152
173
|
let timer;
|
|
153
174
|
const nap = new Promise((resolve) => {
|
|
154
175
|
timer = setTimeout(resolve, ms);
|
|
155
|
-
|
|
176
|
+
if (!holdProcess)
|
|
177
|
+
timer.unref?.();
|
|
156
178
|
});
|
|
157
179
|
// Clear the timer whichever side wins, so a stop is never followed by a
|
|
158
180
|
// leftover sweep timer.
|
package/dist/store/base.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { type Task, type TaskRef, type TaskStatus } from "../models.js";
|
|
|
2
2
|
import { type BackpressureOptions } from "../backpressure.js";
|
|
3
3
|
/** Encode a value for a protocol JSON column, raising SerializationError on
|
|
4
4
|
* anything JSON cannot represent. Refuses what JSON.stringify would silently
|
|
5
|
-
* mangle
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* mangle: NaN/Infinity anywhere, undefined/function/symbol inside an array, a
|
|
6
|
+
* top-level undefined that disappears entirely, and the objects that would be
|
|
7
|
+
* written as an empty one (see emptiesItselfOut) — either way the twin SDK
|
|
8
|
+
* reads back something other than what the caller meant. The Python SDK rejects
|
|
9
|
+
* the same classes of value; see its dump_json. */
|
|
9
10
|
export declare function dumpJson(value: unknown): string;
|
|
10
11
|
/** Refuse to run against a store whose protocol major this SDK does not speak.
|
|
11
12
|
* The supported major is a protocol fact, not a dialect one — every backend
|
|
@@ -28,7 +29,8 @@ export interface SubmitInput {
|
|
|
28
29
|
parentId?: string | null;
|
|
29
30
|
rootId?: string | null;
|
|
30
31
|
correlationId?: string | null;
|
|
31
|
-
|
|
32
|
+
/** Run no earlier than this many ms from now. */
|
|
33
|
+
delayMs?: number;
|
|
32
34
|
}
|
|
33
35
|
export interface ListInput {
|
|
34
36
|
status?: TaskStatus | null;
|
|
@@ -109,32 +111,6 @@ export declare function statementParams(sql: string): readonly string[];
|
|
|
109
111
|
* them in one place is what stops SQLite and Postgres from drifting apart in
|
|
110
112
|
* behavior; the shared SQL already stops them from drifting in wording.
|
|
111
113
|
*/
|
|
112
|
-
/**
|
|
113
|
-
* Why `watch` is calling back.
|
|
114
|
-
*
|
|
115
|
-
* `queued` / `done` come from the store's push channel and name what moved;
|
|
116
|
-
* `poll` is the timer saying the watch cannot rule out a change. None of them
|
|
117
|
-
* carries state — the row is the truth.
|
|
118
|
-
*/
|
|
119
|
-
export interface WatchSignal {
|
|
120
|
-
reason: "queued" | "done" | "poll";
|
|
121
|
-
/** The queue a task was queued on. Only on `queued`. */
|
|
122
|
-
queue?: string;
|
|
123
|
-
/** The task that reached a terminal status. Only on `done`. */
|
|
124
|
-
taskId?: string;
|
|
125
|
-
}
|
|
126
|
-
export interface WatchOptions {
|
|
127
|
-
/** Restrict `queued` signals to these queues. Unset watches every queue. */
|
|
128
|
-
queues?: string[];
|
|
129
|
-
/**
|
|
130
|
-
* How often to signal in the absence of a push channel — and, where there is
|
|
131
|
-
* one, how long a dropped listener can go unnoticed. The default trades a
|
|
132
|
-
* dashboard's idle query rate against how stale it may look.
|
|
133
|
-
*/
|
|
134
|
-
pollMs?: number;
|
|
135
|
-
}
|
|
136
|
-
/** See WatchOptions.pollMs. */
|
|
137
|
-
export declare const DEFAULT_WATCH_POLL_MS = 2000;
|
|
138
114
|
export declare abstract class TaskStore {
|
|
139
115
|
/** Set by useBackpressure; null means submit is ungated. */
|
|
140
116
|
private gate;
|
|
@@ -167,25 +143,12 @@ export declare abstract class TaskStore {
|
|
|
167
143
|
* `completeIn` reports that. Postgres implements it; SQLite does not.
|
|
168
144
|
*/
|
|
169
145
|
protected txWithSession?<T>(fn: (fetch: Fetch, session: unknown) => Promise<T>): Promise<T>;
|
|
170
|
-
/**
|
|
171
|
-
* Register for this store's push channel, if it has one; returns an
|
|
172
|
-
* unsubscribe. A store without a push channel does not implement this, and
|
|
173
|
-
* `watch` degrades to its timer alone.
|
|
174
|
-
*/
|
|
175
|
-
protected subscribePush?(onSignal: (signal: WatchSignal) => void): () => void;
|
|
176
146
|
/**
|
|
177
147
|
* Tell a store with a push channel which queues this process will wait on, so
|
|
178
148
|
* it can buffer their notifications and ignore everyone else's. Optional: a
|
|
179
149
|
* store without a push channel has nothing to buffer.
|
|
180
150
|
*/
|
|
181
151
|
protected registerWakeable?(queues: string[]): void;
|
|
182
|
-
/**
|
|
183
|
-
* Nudge the push channel back up if it has dropped. Called from `watch`'s
|
|
184
|
-
* timer, which is the only thing keeping a client-side subscriber alive: a
|
|
185
|
-
* process that never claims never calls claimWake, so without this a listener
|
|
186
|
-
* that died once would never come back there.
|
|
187
|
-
*/
|
|
188
|
-
protected warmPush?(): void;
|
|
189
152
|
/**
|
|
190
153
|
* Whether it is worth opening the claim transaction at all — the read-only
|
|
191
154
|
* `claimable_probe`, which every dialect ships.
|
|
@@ -207,13 +170,31 @@ export declare abstract class TaskStore {
|
|
|
207
170
|
/** Resolves when `taskId` may have gone terminal. Plain ref'd sleep —
|
|
208
171
|
* pollWait awaits it directly, so it is what keeps the process alive. */
|
|
209
172
|
taskDoneWake(_taskId: string, timeoutMs: number): Promise<void>;
|
|
173
|
+
/**
|
|
174
|
+
* Whether this store's driver hands JSON columns back as text.
|
|
175
|
+
*
|
|
176
|
+
* Declared by the store rather than sniffed per value, because for a JSON
|
|
177
|
+
* *string* the two wire forms are indistinguishable: the text form of
|
|
178
|
+
* `"hello"` and the decoded form of `"hello"` are both strings, and treating
|
|
179
|
+
* the decoded one as text parses it twice — `"s3://…"` threw, `"42"` came back
|
|
180
|
+
* as the number 42. See rowToTask.
|
|
181
|
+
*
|
|
182
|
+
* True is the SQLite answer and the safe default: its JSON columns are TEXT,
|
|
183
|
+
* always. PostgresStore replaces it with what its driver actually does, which
|
|
184
|
+
* it measures rather than assumes — an injected executor's driver is the
|
|
185
|
+
* application's choice, not cairnq's.
|
|
186
|
+
*/
|
|
187
|
+
protected jsonIsText: boolean;
|
|
188
|
+
/** rowToTask, told this store's wire form. Every row in this class goes
|
|
189
|
+
* through here so no call site has to remember to pass it. */
|
|
190
|
+
private toTask;
|
|
210
191
|
/**
|
|
211
192
|
* An ownership-checked worker write (heartbeat/progress/succeed/complete/fail).
|
|
212
193
|
* Each statement's WHERE pins worker_id + a live lease, so 0 rows back means
|
|
213
194
|
* the lease was lost — every such write reports it the same way.
|
|
214
195
|
*/
|
|
215
196
|
private ownedWrite;
|
|
216
|
-
private
|
|
197
|
+
private one;
|
|
217
198
|
private static oneRef;
|
|
218
199
|
/**
|
|
219
200
|
* Bound how deep a queue may get before `submit` blocks. Off unless set.
|
|
@@ -260,7 +241,15 @@ export declare abstract class TaskStore {
|
|
|
260
241
|
*/
|
|
261
242
|
purge(input?: PurgeInput): Promise<string[]>;
|
|
262
243
|
/**
|
|
263
|
-
*
|
|
244
|
+
* How many more tasks fit on `queue` under `maxDepth` — 0 once it is full.
|
|
245
|
+
*
|
|
246
|
+
* The cheap half of backpressure: bounded at `maxDepth` index entries, so it
|
|
247
|
+
* stays cheap to ask on every enqueue. Use it directly to shed load or shape
|
|
248
|
+
* a producer; `QueueDepthGate` builds the blocking form on top.
|
|
249
|
+
*/
|
|
250
|
+
queueDepth(queue: string, maxDepth: number): Promise<number>;
|
|
251
|
+
/**
|
|
252
|
+
* Task counts grouped by queue, keyed by status and zero-filled —
|
|
264
253
|
* `(await stats()).default.queued` is the backlog of a queue. A queue appears
|
|
265
254
|
* only while it has rows; terminal tasks keep counting until `purge` removes
|
|
266
255
|
* them.
|
|
@@ -278,37 +267,6 @@ export declare abstract class TaskStore {
|
|
|
278
267
|
* the point.
|
|
279
268
|
*/
|
|
280
269
|
stats(queue?: string): Promise<Record<string, Record<TaskStatus, number>>>;
|
|
281
|
-
/**
|
|
282
|
-
* Call `onSignal` when the tasks on `queues` may have changed — something was
|
|
283
|
-
* queued, or something finished.
|
|
284
|
-
*
|
|
285
|
-
* This is notify-ACCELERATED POLLING, not an event log, and the difference is
|
|
286
|
-
* the whole contract. Where a push channel is available (Postgres LISTEN) an
|
|
287
|
-
* idle watch costs nothing and a signal arrives within milliseconds of the
|
|
288
|
-
* event. Where it is not — a transaction-mode pooler refuses LISTEN, SQLite has
|
|
289
|
-
* no channel at all — the timer alone still delivers `poll` signals, so a
|
|
290
|
-
* consumer that re-reads on every signal is correct in both cases and merely
|
|
291
|
-
* less prompt in one.
|
|
292
|
-
*
|
|
293
|
-
* What it will NOT do is promise that a signal means something happened, or
|
|
294
|
-
* that every event produces its own signal. Treat a signal as "re-read now"
|
|
295
|
-
* and take the truth from `stats()` / `list()` / `get()`, which is where it
|
|
296
|
-
* lives. `reason` is a hint for reading less: a `done` signal names the task,
|
|
297
|
-
* so a dashboard can refresh that row instead of the list.
|
|
298
|
-
*
|
|
299
|
-
* Returns an unsubscribe. The timer is unref'd — watching does not hold a
|
|
300
|
-
* process open.
|
|
301
|
-
*/
|
|
302
|
-
watch(opts: WatchOptions, onSignal: (signal: WatchSignal) => void): () => void;
|
|
303
|
-
/**
|
|
304
|
-
* How many more tasks fit on `queue` under `maxDepth` — 0 once it is full.
|
|
305
|
-
*
|
|
306
|
-
* The cheap half of backpressure: bounded at `maxDepth` index entries, unlike
|
|
307
|
-
* `stats()`, which aggregates the whole table (terminal rows included) and so
|
|
308
|
-
* costs more the longer a database has been running. Use it directly to shed
|
|
309
|
-
* load or shape a producer; `QueueDepthGate` builds the blocking form on top.
|
|
310
|
-
*/
|
|
311
|
-
queueDepth(queue: string, maxDepth: number): Promise<number>;
|
|
312
270
|
/**
|
|
313
271
|
* Take up to `limit` claimable tasks. `names` restricts the claim to task names
|
|
314
272
|
* this caller can actually run — a worker passes its registered handlers.
|