@ultimat3/jobs 1.0.0 → 1.2.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/package.json +5 -5
- package/src/worker.ts +33 -1
- package/src/clock.d.ts +0 -7
- package/src/clock.d.ts.map +0 -1
- package/src/clock.js +0 -21
- package/src/clock.js.map +0 -1
- package/src/driver-memory.d.ts +0 -9
- package/src/driver-memory.d.ts.map +0 -1
- package/src/driver-memory.js +0 -189
- package/src/driver-memory.js.map +0 -1
- package/src/driver-nats.d.ts +0 -7
- package/src/driver-nats.d.ts.map +0 -1
- package/src/driver-nats.js +0 -51
- package/src/driver-nats.js.map +0 -1
- package/src/driver-pg-sql.d.ts +0 -19
- package/src/driver-pg-sql.d.ts.map +0 -1
- package/src/driver-pg-sql.js +0 -160
- package/src/driver-pg-sql.js.map +0 -1
- package/src/driver-pg.d.ts +0 -17
- package/src/driver-pg.d.ts.map +0 -1
- package/src/driver-pg.js +0 -246
- package/src/driver-pg.js.map +0 -1
- package/src/driver-redis.d.ts +0 -7
- package/src/driver-redis.d.ts.map +0 -1
- package/src/driver-redis.js +0 -54
- package/src/driver-redis.js.map +0 -1
- package/src/driver.d.ts +0 -114
- package/src/driver.d.ts.map +0 -1
- package/src/driver.js +0 -14
- package/src/driver.js.map +0 -1
- package/src/errors.d.ts +0 -63
- package/src/errors.d.ts.map +0 -1
- package/src/errors.js +0 -105
- package/src/errors.js.map +0 -1
- package/src/events.d.ts +0 -35
- package/src/events.d.ts.map +0 -1
- package/src/events.js +0 -92
- package/src/events.js.map +0 -1
- package/src/index.d.ts +0 -32
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -18
- package/src/index.js.map +0 -1
- package/src/inspect.d.ts +0 -82
- package/src/inspect.d.ts.map +0 -1
- package/src/inspect.js +0 -113
- package/src/inspect.js.map +0 -1
- package/src/job.d.ts +0 -71
- package/src/job.d.ts.map +0 -1
- package/src/job.js +0 -99
- package/src/job.js.map +0 -1
- package/src/limits.d.ts +0 -47
- package/src/limits.d.ts.map +0 -1
- package/src/limits.js +0 -0
- package/src/limits.js.map +0 -1
- package/src/outbox.d.ts +0 -81
- package/src/outbox.d.ts.map +0 -1
- package/src/outbox.js +0 -202
- package/src/outbox.js.map +0 -1
- package/src/retry.d.ts +0 -40
- package/src/retry.d.ts.map +0 -1
- package/src/retry.js +0 -59
- package/src/retry.js.map +0 -1
- package/src/scheduler.d.ts +0 -79
- package/src/scheduler.d.ts.map +0 -1
- package/src/scheduler.js +0 -183
- package/src/scheduler.js.map +0 -1
- package/src/steps.d.ts +0 -86
- package/src/steps.d.ts.map +0 -1
- package/src/steps.js +0 -227
- package/src/steps.js.map +0 -1
- package/src/worker.d.ts +0 -68
- package/src/worker.d.ts.map +0 -1
- package/src/worker.js +0 -273
- package/src/worker.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/jobs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Durable background work: steps, transactional outbox, cron tasks, one driver interface",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"test": "bun test"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@ultimat3/core": "1.
|
|
34
|
-
"@ultimat3/entity": "1.
|
|
35
|
-
"@ultimat3/schema": "1.
|
|
36
|
-
"@ultimat3/time": "1.
|
|
33
|
+
"@ultimat3/core": "1.2.0",
|
|
34
|
+
"@ultimat3/entity": "1.2.0",
|
|
35
|
+
"@ultimat3/schema": "1.2.0",
|
|
36
|
+
"@ultimat3/time": "1.2.0"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/worker.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// deploy turns "at least once" into "always twice", so draining is on by default.
|
|
5
5
|
|
|
6
6
|
import type { Clock, Ctx } from '@ultimat3/core';
|
|
7
|
-
import { logger, onShutdown, uuid, withSpan } from '@ultimat3/core';
|
|
7
|
+
import { logger, onShutdown, recordQueueDepth, uuid, withSpan } from '@ultimat3/core';
|
|
8
8
|
import { nowMs } from './clock';
|
|
9
9
|
import type { ClaimedJob, JobDriver, QueueStats } from './driver';
|
|
10
10
|
import { DEFAULT_QUEUE, DEFAULT_VISIBILITY_TIMEOUT_MS } from './driver';
|
|
@@ -18,6 +18,14 @@ import { nextRetry } from './retry';
|
|
|
18
18
|
import type { EventLookup, StepRecord } from './steps';
|
|
19
19
|
import { createStepRunner, isStepSuspension } from './steps';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* How often the claim loop republishes `queue_depth`. Its own interval, not `pollIntervalMs`:
|
|
23
|
+
* `driver.stats()` is an aggregate over the whole jobs table and a scrape reads the gauge every
|
|
24
|
+
* ~15s, so publishing at the poll rate would multiply the queue's read load by sixty to write the
|
|
25
|
+
* same number sixty times.
|
|
26
|
+
*/
|
|
27
|
+
const QUEUE_DEPTH_INTERVAL_MS = 15_000;
|
|
28
|
+
|
|
21
29
|
export type JobOutcome = 'completed' | 'suspended' | 'retried' | 'dead-lettered';
|
|
22
30
|
|
|
23
31
|
export interface JobExecution {
|
|
@@ -205,6 +213,29 @@ export function createWorker(options: WorkerOptions): Worker {
|
|
|
205
213
|
let failed = 0;
|
|
206
214
|
let suspended = 0;
|
|
207
215
|
let deadLettered = 0;
|
|
216
|
+
let depthPublishedAt = Number.NEGATIVE_INFINITY;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* This package's ONE metrics call site: the `queue_depth` series `docker/helm`'s worker HPA
|
|
220
|
+
* scales on. `ready` and not `ready + delayed` — the gauge means "waiting to be picked up", and
|
|
221
|
+
* a job parked until Tuesday is not backlog no matter how many workers are added. Every queue
|
|
222
|
+
* the driver reports, not only the ones this process serves, because depth is the queue's fact
|
|
223
|
+
* and a queue no pod published is a queue no autoscaler can see.
|
|
224
|
+
*/
|
|
225
|
+
const publishQueueDepth = async (): Promise<void> => {
|
|
226
|
+
const now = nowMs(options.clock);
|
|
227
|
+
if (now - depthPublishedAt < QUEUE_DEPTH_INTERVAL_MS) return;
|
|
228
|
+
depthPublishedAt = now;
|
|
229
|
+
try {
|
|
230
|
+
for (const stat of await options.driver.stats()) recordQueueDepth(stat.queue, stat.ready);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
// Instrumentation never costs a tick: a queue that cannot be measured must still be worked.
|
|
233
|
+
logger.warn('jobs.worker.depth-failed', {
|
|
234
|
+
workerId,
|
|
235
|
+
error: error instanceof Error ? error.message : String(error),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
};
|
|
208
239
|
|
|
209
240
|
const runClaimed = async (claimed: ClaimedJob): Promise<JobExecution> => {
|
|
210
241
|
const handle = getJob(claimed.name);
|
|
@@ -249,6 +280,7 @@ export function createWorker(options: WorkerOptions): Worker {
|
|
|
249
280
|
|
|
250
281
|
const tick = async (): Promise<readonly JobExecution[]> => {
|
|
251
282
|
if (state === 'draining' || state === 'stopped') return [];
|
|
283
|
+
await publishQueueDepth();
|
|
252
284
|
const results: JobExecution[] = [];
|
|
253
285
|
|
|
254
286
|
for (const queue of queues) {
|
package/src/clock.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Clock } from '@ultimat3/core';
|
|
2
|
-
export type DurationInput = string | number;
|
|
3
|
-
/** A Clock may hand back a `Date` or epoch ms; scheduling needs one comparable number. */
|
|
4
|
-
export declare function nowMs(clock?: Clock): number;
|
|
5
|
-
/** `'3d'` | `'30s'` | `1500` -> ms. Numbers pass through so callers may stay explicit. */
|
|
6
|
-
export declare function toMs(duration: DurationInput): number;
|
|
7
|
-
//# sourceMappingURL=clock.d.ts.map
|
package/src/clock.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["clock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAI5C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,0FAA0F;AAC1F,wBAAgB,KAAK,CAAC,KAAK,GAAE,KAAmB,GAAG,MAAM,CAIxD;AAED,0FAA0F;AAC1F,wBAAgB,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAKpD"}
|
package/src/clock.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Internal: every scheduling decision in this package is made in epoch ms against an
|
|
2
|
-
// injected Clock, so tests never sleep and a frozen clock cannot be bypassed.
|
|
3
|
-
import { systemClock } from '@ultimat3/core';
|
|
4
|
-
import { parseDuration } from '@ultimat3/time';
|
|
5
|
-
/** A Clock may hand back a `Date` or epoch ms; scheduling needs one comparable number. */
|
|
6
|
-
export function nowMs(clock = systemClock) {
|
|
7
|
-
const reading = clock.now();
|
|
8
|
-
if (reading instanceof Date)
|
|
9
|
-
return reading.getTime();
|
|
10
|
-
return Number(reading);
|
|
11
|
-
}
|
|
12
|
-
/** `'3d'` | `'30s'` | `1500` -> ms. Numbers pass through so callers may stay explicit. */
|
|
13
|
-
export function toMs(duration) {
|
|
14
|
-
if (typeof duration === 'number')
|
|
15
|
-
return duration;
|
|
16
|
-
const parsed = parseDuration(duration);
|
|
17
|
-
if (parsed instanceof Date)
|
|
18
|
-
return parsed.getTime();
|
|
19
|
-
return Number(parsed);
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=clock.js.map
|
package/src/clock.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clock.js","sourceRoot":"","sources":["clock.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,8EAA8E;AAG9E,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAI/C,0FAA0F;AAC1F,MAAM,UAAU,KAAK,CAAC,KAAK,GAAU,WAAW;IAC9C,MAAM,OAAO,GAAY,KAAK,CAAC,GAAG,EAAE,CAAC;IACrC,IAAI,OAAO,YAAY,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,IAAI,CAAC,QAAuB;IAC1C,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAClD,MAAM,MAAM,GAAY,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,MAAM,YAAY,IAAI;QAAE,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;IACpD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC"}
|
package/src/driver-memory.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Clock } from '@ultimat3/core';
|
|
2
|
-
import type { JobDriver } from './driver';
|
|
3
|
-
import type { StepStore } from './steps';
|
|
4
|
-
export interface MemoryDriverOptions {
|
|
5
|
-
readonly clock?: Clock;
|
|
6
|
-
readonly steps?: StepStore;
|
|
7
|
-
}
|
|
8
|
-
export declare function createMemoryDriver(options?: MemoryDriverOptions): JobDriver;
|
|
9
|
-
//# sourceMappingURL=driver-memory.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-memory.d.ts","sourceRoot":"","sources":["driver-memory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C,OAAO,KAAK,EAKV,SAAS,EAMV,MAAM,UAAU,CAAC;AAGlB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC5B;AAID,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,SAAS,CAwL/E"}
|
package/src/driver-memory.js
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
// In-process driver for `x dev` and tests: same semantics as pg (visibility timeout,
|
|
2
|
-
// idempotency dedupe, dead-letter) with zero infrastructure, so a test suite exercises the
|
|
3
|
-
// real claim/ack/nack paths rather than a mock that always succeeds.
|
|
4
|
-
import { assert, systemClock, uuid } from '@ultimat3/core';
|
|
5
|
-
import { nowMs } from './clock';
|
|
6
|
-
import { DEFAULT_QUEUE } from './driver';
|
|
7
|
-
import { JobDuplicateError } from './errors';
|
|
8
|
-
import { createMemoryStepStore } from './steps';
|
|
9
|
-
const LIVE_STATES = new Set(['ready', 'delayed', 'running', 'suspended']);
|
|
10
|
-
export function createMemoryDriver(options = {}) {
|
|
11
|
-
const clock = options.clock ?? systemClock;
|
|
12
|
-
const steps = options.steps ?? createMemoryStepStore();
|
|
13
|
-
const jobs = new Map();
|
|
14
|
-
const liveByKey = (key) => {
|
|
15
|
-
for (const record of jobs.values()) {
|
|
16
|
-
if (record.idempotencyKey === key && LIVE_STATES.has(record.state))
|
|
17
|
-
return record;
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
};
|
|
21
|
-
const update = (id, patch) => {
|
|
22
|
-
const existing = jobs.get(id);
|
|
23
|
-
if (existing === undefined)
|
|
24
|
-
return;
|
|
25
|
-
jobs.set(id, { ...existing, ...patch, updatedAt: nowMs(clock) });
|
|
26
|
-
};
|
|
27
|
-
const introspect = {
|
|
28
|
-
job(jobId) {
|
|
29
|
-
return Promise.resolve(jobs.get(jobId));
|
|
30
|
-
},
|
|
31
|
-
list(filter = {}) {
|
|
32
|
-
const rows = [...jobs.values()]
|
|
33
|
-
.filter((record) => filter.queue === undefined || record.queue === filter.queue)
|
|
34
|
-
.filter((record) => filter.name === undefined || record.name === filter.name)
|
|
35
|
-
.filter((record) => filter.state === undefined || record.state === filter.state)
|
|
36
|
-
.sort((a, b) => a.createdAt - b.createdAt)
|
|
37
|
-
.slice(0, filter.limit ?? 100);
|
|
38
|
-
return Promise.resolve(rows);
|
|
39
|
-
},
|
|
40
|
-
deadLetters(limit = 100) {
|
|
41
|
-
const rows = [...jobs.values()]
|
|
42
|
-
.filter((record) => record.state === 'dead')
|
|
43
|
-
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
44
|
-
.slice(0, limit);
|
|
45
|
-
return Promise.resolve(rows);
|
|
46
|
-
},
|
|
47
|
-
async requeue(jobId, requeueOptions) {
|
|
48
|
-
const existing = jobs.get(jobId);
|
|
49
|
-
assert(existing !== undefined, `no job ${jobId} in the memory driver`, 'requeue a job id returned by enqueue() or stats() — the memory driver holds no state across processes, so an id from another run will not resolve');
|
|
50
|
-
const record = existing;
|
|
51
|
-
if (requeueOptions?.fromStep !== undefined) {
|
|
52
|
-
// Drop the target step so it re-executes; earlier steps stay memoized.
|
|
53
|
-
await steps.del(record.runId, requeueOptions.fromStep);
|
|
54
|
-
}
|
|
55
|
-
update(jobId, { state: 'ready', attempt: 0, runAt: nowMs(clock) });
|
|
56
|
-
const next = jobs.get(jobId);
|
|
57
|
-
return next ?? record;
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
return {
|
|
61
|
-
name: 'memory',
|
|
62
|
-
steps,
|
|
63
|
-
introspect,
|
|
64
|
-
enqueue(request) {
|
|
65
|
-
const existing = liveByKey(request.idempotencyKey);
|
|
66
|
-
if (existing !== undefined) {
|
|
67
|
-
if (request.onConflict === 'error') {
|
|
68
|
-
throw new JobDuplicateError({
|
|
69
|
-
job: request.name,
|
|
70
|
-
idempotencyKey: request.idempotencyKey,
|
|
71
|
-
existingId: existing.id,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
return Promise.resolve({ id: existing.id, runId: existing.runId, deduped: true });
|
|
75
|
-
}
|
|
76
|
-
const at = nowMs(clock);
|
|
77
|
-
const runAt = request.runAt ?? at;
|
|
78
|
-
const record = {
|
|
79
|
-
id: uuid(),
|
|
80
|
-
name: request.name,
|
|
81
|
-
queue: request.queue || DEFAULT_QUEUE,
|
|
82
|
-
input: request.input,
|
|
83
|
-
idempotencyKey: request.idempotencyKey,
|
|
84
|
-
runId: request.runId ?? uuid(),
|
|
85
|
-
attempt: 0,
|
|
86
|
-
maxAttempts: request.maxAttempts,
|
|
87
|
-
state: runAt > at ? 'delayed' : 'ready',
|
|
88
|
-
runAt,
|
|
89
|
-
createdAt: at,
|
|
90
|
-
updatedAt: at,
|
|
91
|
-
...(request.tenantId === undefined ? {} : { tenantId: request.tenantId }),
|
|
92
|
-
};
|
|
93
|
-
jobs.set(record.id, record);
|
|
94
|
-
return Promise.resolve({ id: record.id, runId: record.runId, deduped: false });
|
|
95
|
-
},
|
|
96
|
-
claim(claimOptions) {
|
|
97
|
-
const at = nowMs(clock);
|
|
98
|
-
const wanted = new Set(claimOptions.queues);
|
|
99
|
-
const claimable = [...jobs.values()]
|
|
100
|
-
.filter((record) => wanted.size === 0 || wanted.has(record.queue))
|
|
101
|
-
.filter((record) => {
|
|
102
|
-
if (record.runAt > at)
|
|
103
|
-
return false;
|
|
104
|
-
if (record.state === 'ready' || record.state === 'delayed')
|
|
105
|
-
return true;
|
|
106
|
-
if (record.state === 'suspended')
|
|
107
|
-
return true;
|
|
108
|
-
// Lease expiry: a worker that died without ack releases its job here.
|
|
109
|
-
return record.state === 'running' && (record.visibleAt ?? 0) <= at;
|
|
110
|
-
})
|
|
111
|
-
.sort((a, b) => a.runAt - b.runAt)
|
|
112
|
-
.slice(0, claimOptions.limit);
|
|
113
|
-
const out = [];
|
|
114
|
-
for (const record of claimable) {
|
|
115
|
-
const claimed = {
|
|
116
|
-
...record,
|
|
117
|
-
state: 'running',
|
|
118
|
-
attempt: record.attempt + 1,
|
|
119
|
-
claimedBy: claimOptions.workerId,
|
|
120
|
-
claimedAt: at,
|
|
121
|
-
visibleAt: at + claimOptions.visibilityTimeoutMs,
|
|
122
|
-
updatedAt: at,
|
|
123
|
-
};
|
|
124
|
-
jobs.set(record.id, claimed);
|
|
125
|
-
out.push(claimed);
|
|
126
|
-
}
|
|
127
|
-
return Promise.resolve(out);
|
|
128
|
-
},
|
|
129
|
-
ack(jobId) {
|
|
130
|
-
update(jobId, { state: 'done' });
|
|
131
|
-
return Promise.resolve();
|
|
132
|
-
},
|
|
133
|
-
nack(jobId, nackOptions) {
|
|
134
|
-
const record = jobs.get(jobId);
|
|
135
|
-
if (record === undefined)
|
|
136
|
-
return Promise.resolve();
|
|
137
|
-
const at = nowMs(clock);
|
|
138
|
-
const counts = nackOptions.countsAsAttempt !== false;
|
|
139
|
-
const patch = {
|
|
140
|
-
state: nackOptions.deadLetter === true ? 'dead' : counts ? 'ready' : 'suspended',
|
|
141
|
-
runAt: at + nackOptions.delayMs,
|
|
142
|
-
// A suspension must not burn an attempt, or a 3-day sleep dead-letters the run.
|
|
143
|
-
attempt: counts ? record.attempt : record.attempt - 1,
|
|
144
|
-
...(nackOptions.error === undefined ? {} : { lastError: nackOptions.error }),
|
|
145
|
-
};
|
|
146
|
-
update(jobId, patch);
|
|
147
|
-
return Promise.resolve();
|
|
148
|
-
},
|
|
149
|
-
heartbeat(jobId, heartbeatOptions) {
|
|
150
|
-
update(jobId, { visibleAt: nowMs(clock) + heartbeatOptions.visibilityTimeoutMs });
|
|
151
|
-
return Promise.resolve();
|
|
152
|
-
},
|
|
153
|
-
stats() {
|
|
154
|
-
const at = nowMs(clock);
|
|
155
|
-
const byQueue = new Map();
|
|
156
|
-
for (const record of jobs.values()) {
|
|
157
|
-
const current = byQueue.get(record.queue) ?? {
|
|
158
|
-
queue: record.queue,
|
|
159
|
-
ready: 0,
|
|
160
|
-
delayed: 0,
|
|
161
|
-
running: 0,
|
|
162
|
-
suspended: 0,
|
|
163
|
-
dead: 0,
|
|
164
|
-
oldestReadyMs: 0,
|
|
165
|
-
};
|
|
166
|
-
const next = { ...current };
|
|
167
|
-
if (record.state === 'ready' && record.runAt <= at) {
|
|
168
|
-
next.ready += 1;
|
|
169
|
-
next.oldestReadyMs = Math.max(next.oldestReadyMs, at - record.runAt);
|
|
170
|
-
}
|
|
171
|
-
else if (record.state === 'ready' || record.state === 'delayed')
|
|
172
|
-
next.delayed += 1;
|
|
173
|
-
else if (record.state === 'running')
|
|
174
|
-
next.running += 1;
|
|
175
|
-
else if (record.state === 'suspended')
|
|
176
|
-
next.suspended += 1;
|
|
177
|
-
else if (record.state === 'dead')
|
|
178
|
-
next.dead += 1;
|
|
179
|
-
byQueue.set(record.queue, next);
|
|
180
|
-
}
|
|
181
|
-
return Promise.resolve([...byQueue.values()].sort((a, b) => a.queue.localeCompare(b.queue)));
|
|
182
|
-
},
|
|
183
|
-
close() {
|
|
184
|
-
jobs.clear();
|
|
185
|
-
return Promise.resolve();
|
|
186
|
-
},
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
//# sourceMappingURL=driver-memory.js.map
|
package/src/driver-memory.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-memory.js","sourceRoot":"","sources":["driver-memory.ts"],"names":[],"mappings":"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"}
|
package/src/driver-nats.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { JobDriver } from './driver';
|
|
2
|
-
export interface NatsDriverOptions {
|
|
3
|
-
readonly servers?: readonly string[];
|
|
4
|
-
readonly streamPrefix?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function createNatsDriver(_options?: NatsDriverOptions): JobDriver;
|
|
7
|
-
//# sourceMappingURL=driver-nats.d.ts.map
|
package/src/driver-nats.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-nats.d.ts","sourceRoot":"","sources":["driver-nats.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAKV,SAAS,EAGV,MAAM,UAAU,CAAC;AA4BlB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,iBAAsB,GAAG,SAAS,CAuB5E"}
|
package/src/driver-nats.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// NATS driver — interface-complete, not implemented. The intended mapping, so the eventual
|
|
2
|
-
// implementation has no design decisions left: a JetStream work-queue stream per job queue,
|
|
3
|
-
// a durable pull consumer per worker pool (`fetch` == claim, `ack`/`nak` map 1:1),
|
|
4
|
-
// `ack_wait` as the visibility timeout, and a KV bucket for step records.
|
|
5
|
-
import { JobsNotImplementedError } from './errors';
|
|
6
|
-
const FIX = 'use driver: "pg" (default) or "memory" — see docs/jobs/drivers.md#nats';
|
|
7
|
-
const unavailable = (method) => {
|
|
8
|
-
throw new JobsNotImplementedError({ feature: `nats jobs driver (${method})`, fix: FIX });
|
|
9
|
-
};
|
|
10
|
-
const natsStepStore = () => ({
|
|
11
|
-
get(_runId, _name) {
|
|
12
|
-
return unavailable('steps.get');
|
|
13
|
-
},
|
|
14
|
-
put(_record) {
|
|
15
|
-
return unavailable('steps.put');
|
|
16
|
-
},
|
|
17
|
-
list(_runId) {
|
|
18
|
-
return unavailable('steps.list');
|
|
19
|
-
},
|
|
20
|
-
del(_runId, _name) {
|
|
21
|
-
return unavailable('steps.del');
|
|
22
|
-
},
|
|
23
|
-
clear(_runId) {
|
|
24
|
-
return unavailable('steps.clear');
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
export function createNatsDriver(_options = {}) {
|
|
28
|
-
return {
|
|
29
|
-
name: 'nats',
|
|
30
|
-
steps: natsStepStore(),
|
|
31
|
-
enqueue(_request) {
|
|
32
|
-
return unavailable('enqueue');
|
|
33
|
-
},
|
|
34
|
-
claim(_options) {
|
|
35
|
-
return unavailable('claim');
|
|
36
|
-
},
|
|
37
|
-
ack(_jobId) {
|
|
38
|
-
return unavailable('ack');
|
|
39
|
-
},
|
|
40
|
-
nack(_jobId, _options) {
|
|
41
|
-
return unavailable('nack');
|
|
42
|
-
},
|
|
43
|
-
heartbeat(_jobId, _options) {
|
|
44
|
-
return unavailable('heartbeat');
|
|
45
|
-
},
|
|
46
|
-
stats() {
|
|
47
|
-
return unavailable('stats');
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=driver-nats.js.map
|
package/src/driver-nats.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-nats.js","sourceRoot":"","sources":["driver-nats.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,4FAA4F;AAC5F,mFAAmF;AACnF,0EAA0E;AAW1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAGnD,MAAM,GAAG,GAAG,wEAAwE,CAAC;AAErF,MAAM,WAAW,GAAG,CAAC,MAAc,EAAS,EAAE;IAC5C,MAAM,IAAI,uBAAuB,CAAC,EAAE,OAAO,EAAE,qBAAqB,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AAC3F,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,GAAc,EAAE,CAAC,CAAC;IACtC,GAAG,CAAC,MAAc,EAAE,KAAa;QAC/B,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IACD,GAAG,CAAC,OAAmB;QACrB,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,MAAc;QACjB,OAAO,WAAW,CAAC,YAAY,CAAC,CAAC;IACnC,CAAC;IACD,GAAG,CAAC,MAAc,EAAE,KAAa;QAC/B,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IACD,KAAK,CAAC,MAAc;QAClB,OAAO,WAAW,CAAC,aAAa,CAAC,CAAC;IACpC,CAAC;CACF,CAAC,CAAC;AAOH,MAAM,UAAU,gBAAgB,CAAC,QAAQ,GAAsB,EAAE;IAC/D,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,aAAa,EAAE;QACtB,OAAO,CAAC,QAAwB;YAC9B,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,QAAsB;YAC1B,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,GAAG,CAAC,MAAc;YAChB,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,MAAc,EAAE,QAAqB;YACxC,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QACD,SAAS,CAAC,MAAc,EAAE,QAAkD;YAC1E,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;QAClC,CAAC;QACD,KAAK;YACH,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/src/driver-pg-sql.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export declare const SQL_JOBS_TABLE: string;
|
|
2
|
-
export declare const SQL_ENQUEUE: string;
|
|
3
|
-
export declare const SQL_FIND_LIVE_BY_KEY: string;
|
|
4
|
-
/**
|
|
5
|
-
* The claim. SKIP LOCKED is the whole design: without it, worker 2 blocks on worker 1's row
|
|
6
|
-
* lock and throughput collapses to one worker. `visible_at` in the predicate reclaims leases
|
|
7
|
-
* abandoned by a crashed worker.
|
|
8
|
-
*/
|
|
9
|
-
export declare const SQL_CLAIM: string;
|
|
10
|
-
export declare const SQL_ACK: string;
|
|
11
|
-
export declare const SQL_NACK: string;
|
|
12
|
-
export declare const SQL_HEARTBEAT: string;
|
|
13
|
-
export declare const SQL_STATS: string;
|
|
14
|
-
/** Scheduler leader election. Session-scoped, so a crashed node's lock releases itself. */
|
|
15
|
-
export declare const SQL_TRY_ADVISORY_LOCK = "select pg_try_advisory_lock($1) as locked";
|
|
16
|
-
export declare const SQL_ADVISORY_UNLOCK = "select pg_advisory_unlock($1) as unlocked";
|
|
17
|
-
export declare const SQL_STEP_GET: string;
|
|
18
|
-
export declare const SQL_STEP_PUT: string;
|
|
19
|
-
//# sourceMappingURL=driver-pg-sql.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-pg-sql.d.ts","sourceRoot":"","sources":["driver-pg-sql.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,cAAc,QA4CnB,CAAC;AAET,eAAO,MAAM,WAAW,QAWhB,CAAC;AAET,eAAO,MAAM,oBAAoB,QAKzB,CAAC;AAET;;;;GAIG;AACH,eAAO,MAAM,SAAS,QA4Bd,CAAC;AAET,eAAO,MAAM,OAAO,QAIZ,CAAC;AAET,eAAO,MAAM,QAAQ,QAUb,CAAC;AAET,eAAO,MAAM,aAAa,QAIlB,CAAC;AAET,eAAO,MAAM,SAAS,QAYd,CAAC;AAET,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,8CAA8C,CAAC;AACjF,eAAO,MAAM,mBAAmB,8CAA8C,CAAC;AAE/E,eAAO,MAAM,YAAY,QAOjB,CAAC;AAET,eAAO,MAAM,YAAY,QAYjB,CAAC"}
|
package/src/driver-pg-sql.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
// Every statement the Postgres driver runs, spelled out as template strings on purpose: an
|
|
2
|
-
// agent debugging a stuck queue should be able to read, run and correct the exact statement
|
|
3
|
-
// it saw in a log, without reassembling it from a builder. Kept beside the driver rather than
|
|
4
|
-
// inside it so the driver file stays the control flow and this one stays the wire format.
|
|
5
|
-
export const SQL_JOBS_TABLE = `
|
|
6
|
-
create table if not exists x_jobs (
|
|
7
|
-
id uuid primary key,
|
|
8
|
-
name text not null,
|
|
9
|
-
queue text not null default 'default',
|
|
10
|
-
input jsonb not null,
|
|
11
|
-
idempotency_key text not null,
|
|
12
|
-
run_id uuid not null,
|
|
13
|
-
attempt int not null default 0,
|
|
14
|
-
max_attempts int not null default 3,
|
|
15
|
-
state text not null default 'ready',
|
|
16
|
-
run_at timestamptz not null default now(),
|
|
17
|
-
visible_at timestamptz,
|
|
18
|
-
claimed_by text,
|
|
19
|
-
last_error text,
|
|
20
|
-
tenant_id text,
|
|
21
|
-
created_at timestamptz not null default now(),
|
|
22
|
-
updated_at timestamptz not null default now()
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
-- Partial unique index: one LIVE job per idempotency key. Completed rows stay for history,
|
|
26
|
-
-- so re-running the same work tomorrow is allowed and re-delivering it today is not.
|
|
27
|
-
create unique index if not exists x_jobs_idempotency_live_idx
|
|
28
|
-
on x_jobs (idempotency_key)
|
|
29
|
-
where state in ('ready', 'delayed', 'running', 'suspended');
|
|
30
|
-
|
|
31
|
-
create index if not exists x_jobs_claim_idx
|
|
32
|
-
on x_jobs (queue, run_at)
|
|
33
|
-
where state in ('ready', 'delayed', 'suspended');
|
|
34
|
-
|
|
35
|
-
create table if not exists x_job_steps (
|
|
36
|
-
run_id uuid not null,
|
|
37
|
-
name text not null,
|
|
38
|
-
status text not null,
|
|
39
|
-
output jsonb,
|
|
40
|
-
started_at timestamptz not null default now(),
|
|
41
|
-
completed_at timestamptz,
|
|
42
|
-
wake_at timestamptz,
|
|
43
|
-
event text,
|
|
44
|
-
correlation_key text,
|
|
45
|
-
attempts int not null default 1,
|
|
46
|
-
error text,
|
|
47
|
-
primary key (run_id, name)
|
|
48
|
-
);
|
|
49
|
-
`.trim();
|
|
50
|
-
export const SQL_ENQUEUE = `
|
|
51
|
-
insert into x_jobs
|
|
52
|
-
(id, name, queue, input, idempotency_key, run_id, max_attempts, state, run_at, tenant_id)
|
|
53
|
-
values
|
|
54
|
-
($1, $2, $3, $4::jsonb, $5, $6, $7,
|
|
55
|
-
case when to_timestamp($8 / 1000.0) > now() then 'delayed' else 'ready' end,
|
|
56
|
-
to_timestamp($8 / 1000.0), $9)
|
|
57
|
-
on conflict (idempotency_key)
|
|
58
|
-
where state in ('ready', 'delayed', 'running', 'suspended')
|
|
59
|
-
do nothing
|
|
60
|
-
returning id, run_id
|
|
61
|
-
`.trim();
|
|
62
|
-
export const SQL_FIND_LIVE_BY_KEY = `
|
|
63
|
-
select id, run_id from x_jobs
|
|
64
|
-
where idempotency_key = $1
|
|
65
|
-
and state in ('ready', 'delayed', 'running', 'suspended')
|
|
66
|
-
limit 1
|
|
67
|
-
`.trim();
|
|
68
|
-
/**
|
|
69
|
-
* The claim. SKIP LOCKED is the whole design: without it, worker 2 blocks on worker 1's row
|
|
70
|
-
* lock and throughput collapses to one worker. `visible_at` in the predicate reclaims leases
|
|
71
|
-
* abandoned by a crashed worker.
|
|
72
|
-
*/
|
|
73
|
-
export const SQL_CLAIM = `
|
|
74
|
-
with claimed as (
|
|
75
|
-
select id
|
|
76
|
-
from x_jobs
|
|
77
|
-
where queue = any($1::text[])
|
|
78
|
-
and run_at <= now()
|
|
79
|
-
and (
|
|
80
|
-
state in ('ready', 'delayed', 'suspended')
|
|
81
|
-
or (state = 'running' and visible_at <= now())
|
|
82
|
-
)
|
|
83
|
-
order by run_at
|
|
84
|
-
limit $2
|
|
85
|
-
for update skip locked
|
|
86
|
-
)
|
|
87
|
-
update x_jobs j
|
|
88
|
-
set state = 'running',
|
|
89
|
-
attempt = j.attempt + 1,
|
|
90
|
-
claimed_by = $3,
|
|
91
|
-
visible_at = now() + ($4::bigint * interval '1 millisecond'),
|
|
92
|
-
updated_at = now()
|
|
93
|
-
from claimed c
|
|
94
|
-
where j.id = c.id
|
|
95
|
-
returning j.id, j.name, j.queue, j.input, j.idempotency_key, j.run_id, j.attempt,
|
|
96
|
-
j.max_attempts, j.state, j.tenant_id, j.last_error, j.claimed_by,
|
|
97
|
-
(extract(epoch from j.run_at) * 1000)::bigint as run_at,
|
|
98
|
-
(extract(epoch from j.visible_at) * 1000)::bigint as visible_at,
|
|
99
|
-
(extract(epoch from j.created_at) * 1000)::bigint as created_at,
|
|
100
|
-
(extract(epoch from j.updated_at) * 1000)::bigint as updated_at
|
|
101
|
-
`.trim();
|
|
102
|
-
export const SQL_ACK = `
|
|
103
|
-
update x_jobs
|
|
104
|
-
set state = 'done', visible_at = null, claimed_by = null, updated_at = now()
|
|
105
|
-
where id = $1
|
|
106
|
-
`.trim();
|
|
107
|
-
export const SQL_NACK = `
|
|
108
|
-
update x_jobs
|
|
109
|
-
set state = $2,
|
|
110
|
-
attempt = case when $3::boolean then attempt else greatest(attempt - 1, 0) end,
|
|
111
|
-
run_at = now() + ($4::bigint * interval '1 millisecond'),
|
|
112
|
-
visible_at = null,
|
|
113
|
-
claimed_by = null,
|
|
114
|
-
last_error = coalesce($5, last_error),
|
|
115
|
-
updated_at = now()
|
|
116
|
-
where id = $1
|
|
117
|
-
`.trim();
|
|
118
|
-
export const SQL_HEARTBEAT = `
|
|
119
|
-
update x_jobs
|
|
120
|
-
set visible_at = now() + ($2::bigint * interval '1 millisecond'), updated_at = now()
|
|
121
|
-
where id = $1 and state = 'running'
|
|
122
|
-
`.trim();
|
|
123
|
-
export const SQL_STATS = `
|
|
124
|
-
select queue,
|
|
125
|
-
count(*) filter (where state = 'ready' and run_at <= now()) as ready,
|
|
126
|
-
count(*) filter (where state = 'delayed' or run_at > now()) as delayed,
|
|
127
|
-
count(*) filter (where state = 'running') as running,
|
|
128
|
-
count(*) filter (where state = 'suspended') as suspended,
|
|
129
|
-
count(*) filter (where state = 'dead') as dead,
|
|
130
|
-
coalesce(max(extract(epoch from now() - run_at)) filter
|
|
131
|
-
(where state = 'ready' and run_at <= now()), 0) * 1000 as oldest_ready_ms
|
|
132
|
-
from x_jobs
|
|
133
|
-
group by queue
|
|
134
|
-
order by queue
|
|
135
|
-
`.trim();
|
|
136
|
-
/** Scheduler leader election. Session-scoped, so a crashed node's lock releases itself. */
|
|
137
|
-
export const SQL_TRY_ADVISORY_LOCK = 'select pg_try_advisory_lock($1) as locked';
|
|
138
|
-
export const SQL_ADVISORY_UNLOCK = 'select pg_advisory_unlock($1) as unlocked';
|
|
139
|
-
export const SQL_STEP_GET = `
|
|
140
|
-
select run_id, name, status, output, attempts, error,
|
|
141
|
-
(extract(epoch from started_at) * 1000)::bigint as started_at,
|
|
142
|
-
(extract(epoch from completed_at) * 1000)::bigint as completed_at,
|
|
143
|
-
(extract(epoch from wake_at) * 1000)::bigint as wake_at,
|
|
144
|
-
event, correlation_key
|
|
145
|
-
from x_job_steps where run_id = $1 and name = $2
|
|
146
|
-
`.trim();
|
|
147
|
-
export const SQL_STEP_PUT = `
|
|
148
|
-
insert into x_job_steps
|
|
149
|
-
(run_id, name, status, output, started_at, completed_at, wake_at, event,
|
|
150
|
-
correlation_key, attempts, error)
|
|
151
|
-
values ($1, $2, $3, $4::jsonb, to_timestamp($5 / 1000.0),
|
|
152
|
-
case when $6::bigint is null then null else to_timestamp($6 / 1000.0) end,
|
|
153
|
-
case when $7::bigint is null then null else to_timestamp($7 / 1000.0) end,
|
|
154
|
-
$8, $9, $10, $11)
|
|
155
|
-
on conflict (run_id, name) do update
|
|
156
|
-
set status = excluded.status, output = excluded.output,
|
|
157
|
-
completed_at = excluded.completed_at, wake_at = excluded.wake_at,
|
|
158
|
-
attempts = excluded.attempts, error = excluded.error
|
|
159
|
-
`.trim();
|
|
160
|
-
//# sourceMappingURL=driver-pg-sql.js.map
|
package/src/driver-pg-sql.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-pg-sql.js","sourceRoot":"","sources":["driver-pg-sql.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,4FAA4F;AAC5F,8FAA8F;AAC9F,0FAA0F;AAE1F,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4C7B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;CAW1B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;CAKnC,CAAC,IAAI,EAAE,CAAC;AAET;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BxB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,OAAO,GAAG;;;;CAItB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;CAUvB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,aAAa,GAAG;;;;CAI5B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;CAYxB,CAAC,IAAI,EAAE,CAAC;AAET,2FAA2F;AAC3F,MAAM,CAAC,MAAM,qBAAqB,GAAG,2CAA2C,CAAC;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,2CAA2C,CAAC;AAE/E,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;CAO3B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;CAY3B,CAAC,IAAI,EAAE,CAAC"}
|
package/src/driver-pg.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Clock } from '@ultimat3/core';
|
|
2
|
-
import type { JobDriver } from './driver';
|
|
3
|
-
/** The one thing this driver needs from the DB layer. Satisfied by `Bun.sql` and by a Tx. */
|
|
4
|
-
export interface PgExecutor {
|
|
5
|
-
query<R>(sql: string, params: readonly unknown[]): Promise<readonly R[]>;
|
|
6
|
-
}
|
|
7
|
-
export interface PgDriverOptions {
|
|
8
|
-
readonly executor?: PgExecutor;
|
|
9
|
-
readonly clock?: Clock;
|
|
10
|
-
}
|
|
11
|
-
export declare function createPgDriver(options?: PgDriverOptions): JobDriver;
|
|
12
|
-
/** Advisory-lock leader election, used by the `scheduler` role. */
|
|
13
|
-
export declare function createPgLeader(lockKey: number, options?: PgDriverOptions): {
|
|
14
|
-
acquire(): Promise<boolean>;
|
|
15
|
-
release(): Promise<void>;
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=driver-pg.d.ts.map
|
package/src/driver-pg.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"driver-pg.d.ts","sourceRoot":"","sources":["driver-pg.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C,OAAO,KAAK,EAKV,SAAS,EAMV,MAAM,UAAU,CAAC;AAkBlB,6FAA6F;AAC7F,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAgID,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,SAAS,CAqKvE;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B;IAAE,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAW3D"}
|