@ultimat3/jobs 1.0.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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/package.json +38 -0
  4. package/src/clock.d.ts +7 -0
  5. package/src/clock.d.ts.map +1 -0
  6. package/src/clock.js +21 -0
  7. package/src/clock.js.map +1 -0
  8. package/src/clock.ts +23 -0
  9. package/src/describe.ts +61 -0
  10. package/src/driver-memory.d.ts +9 -0
  11. package/src/driver-memory.d.ts.map +1 -0
  12. package/src/driver-memory.js +189 -0
  13. package/src/driver-memory.js.map +1 -0
  14. package/src/driver-memory.ts +216 -0
  15. package/src/driver-nats.d.ts +7 -0
  16. package/src/driver-nats.d.ts.map +1 -0
  17. package/src/driver-nats.js +51 -0
  18. package/src/driver-nats.js.map +1 -0
  19. package/src/driver-nats.ts +73 -0
  20. package/src/driver-pg-sql.d.ts +19 -0
  21. package/src/driver-pg-sql.d.ts.map +1 -0
  22. package/src/driver-pg-sql.js +160 -0
  23. package/src/driver-pg-sql.js.map +1 -0
  24. package/src/driver-pg-sql.ts +170 -0
  25. package/src/driver-pg.d.ts +17 -0
  26. package/src/driver-pg.d.ts.map +1 -0
  27. package/src/driver-pg.js +246 -0
  28. package/src/driver-pg.js.map +1 -0
  29. package/src/driver-pg.ts +356 -0
  30. package/src/driver-redis.d.ts +7 -0
  31. package/src/driver-redis.d.ts.map +1 -0
  32. package/src/driver-redis.js +54 -0
  33. package/src/driver-redis.js.map +1 -0
  34. package/src/driver-redis.ts +76 -0
  35. package/src/driver.d.ts +114 -0
  36. package/src/driver.d.ts.map +1 -0
  37. package/src/driver.js +14 -0
  38. package/src/driver.js.map +1 -0
  39. package/src/driver.ts +143 -0
  40. package/src/errors.d.ts +63 -0
  41. package/src/errors.d.ts.map +1 -0
  42. package/src/errors.js +105 -0
  43. package/src/errors.js.map +1 -0
  44. package/src/errors.ts +165 -0
  45. package/src/events.d.ts +35 -0
  46. package/src/events.d.ts.map +1 -0
  47. package/src/events.js +92 -0
  48. package/src/events.js.map +1 -0
  49. package/src/events.ts +134 -0
  50. package/src/index.d.ts +32 -0
  51. package/src/index.d.ts.map +1 -0
  52. package/src/index.js +18 -0
  53. package/src/index.js.map +1 -0
  54. package/src/index.ts +172 -0
  55. package/src/inspect.d.ts +82 -0
  56. package/src/inspect.d.ts.map +1 -0
  57. package/src/inspect.js +113 -0
  58. package/src/inspect.js.map +1 -0
  59. package/src/inspect.ts +213 -0
  60. package/src/job.d.ts +71 -0
  61. package/src/job.d.ts.map +1 -0
  62. package/src/job.js +99 -0
  63. package/src/job.js.map +1 -0
  64. package/src/job.ts +261 -0
  65. package/src/limits.d.ts +47 -0
  66. package/src/limits.d.ts.map +1 -0
  67. package/src/limits.js +0 -0
  68. package/src/limits.js.map +1 -0
  69. package/src/limits.ts +0 -0
  70. package/src/outbox.d.ts +81 -0
  71. package/src/outbox.d.ts.map +1 -0
  72. package/src/outbox.js +202 -0
  73. package/src/outbox.js.map +1 -0
  74. package/src/outbox.ts +336 -0
  75. package/src/register.ts +40 -0
  76. package/src/retry.d.ts +40 -0
  77. package/src/retry.d.ts.map +1 -0
  78. package/src/retry.js +59 -0
  79. package/src/retry.js.map +1 -0
  80. package/src/retry.ts +90 -0
  81. package/src/scheduler.d.ts +79 -0
  82. package/src/scheduler.d.ts.map +1 -0
  83. package/src/scheduler.js +183 -0
  84. package/src/scheduler.js.map +1 -0
  85. package/src/scheduler.ts +417 -0
  86. package/src/steps.d.ts +86 -0
  87. package/src/steps.d.ts.map +1 -0
  88. package/src/steps.js +227 -0
  89. package/src/steps.js.map +1 -0
  90. package/src/steps.ts +339 -0
  91. package/src/worker.d.ts +68 -0
  92. package/src/worker.d.ts.map +1 -0
  93. package/src/worker.js +273 -0
  94. package/src/worker.js.map +1 -0
  95. package/src/worker.ts +356 -0
@@ -0,0 +1,170 @@
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
+
6
+ export const SQL_JOBS_TABLE = `
7
+ create table if not exists x_jobs (
8
+ id uuid primary key,
9
+ name text not null,
10
+ queue text not null default 'default',
11
+ input jsonb not null,
12
+ idempotency_key text not null,
13
+ run_id uuid not null,
14
+ attempt int not null default 0,
15
+ max_attempts int not null default 3,
16
+ state text not null default 'ready',
17
+ run_at timestamptz not null default now(),
18
+ visible_at timestamptz,
19
+ claimed_by text,
20
+ last_error text,
21
+ tenant_id text,
22
+ created_at timestamptz not null default now(),
23
+ updated_at timestamptz not null default now()
24
+ );
25
+
26
+ -- Partial unique index: one LIVE job per idempotency key. Completed rows stay for history,
27
+ -- so re-running the same work tomorrow is allowed and re-delivering it today is not.
28
+ create unique index if not exists x_jobs_idempotency_live_idx
29
+ on x_jobs (idempotency_key)
30
+ where state in ('ready', 'delayed', 'running', 'suspended');
31
+
32
+ create index if not exists x_jobs_claim_idx
33
+ on x_jobs (queue, run_at)
34
+ where state in ('ready', 'delayed', 'suspended');
35
+
36
+ create table if not exists x_job_steps (
37
+ run_id uuid not null,
38
+ name text not null,
39
+ status text not null,
40
+ output jsonb,
41
+ started_at timestamptz not null default now(),
42
+ completed_at timestamptz,
43
+ wake_at timestamptz,
44
+ event text,
45
+ correlation_key text,
46
+ attempts int not null default 1,
47
+ error text,
48
+ primary key (run_id, name)
49
+ );
50
+ `.trim();
51
+
52
+ export const SQL_ENQUEUE = `
53
+ insert into x_jobs
54
+ (id, name, queue, input, idempotency_key, run_id, max_attempts, state, run_at, tenant_id)
55
+ values
56
+ ($1, $2, $3, $4::jsonb, $5, $6, $7,
57
+ case when to_timestamp($8 / 1000.0) > now() then 'delayed' else 'ready' end,
58
+ to_timestamp($8 / 1000.0), $9)
59
+ on conflict (idempotency_key)
60
+ where state in ('ready', 'delayed', 'running', 'suspended')
61
+ do nothing
62
+ returning id, run_id
63
+ `.trim();
64
+
65
+ export const SQL_FIND_LIVE_BY_KEY = `
66
+ select id, run_id from x_jobs
67
+ where idempotency_key = $1
68
+ and state in ('ready', 'delayed', 'running', 'suspended')
69
+ limit 1
70
+ `.trim();
71
+
72
+ /**
73
+ * The claim. SKIP LOCKED is the whole design: without it, worker 2 blocks on worker 1's row
74
+ * lock and throughput collapses to one worker. `visible_at` in the predicate reclaims leases
75
+ * abandoned by a crashed worker.
76
+ */
77
+ export const SQL_CLAIM = `
78
+ with claimed as (
79
+ select id
80
+ from x_jobs
81
+ where queue = any($1::text[])
82
+ and run_at <= now()
83
+ and (
84
+ state in ('ready', 'delayed', 'suspended')
85
+ or (state = 'running' and visible_at <= now())
86
+ )
87
+ order by run_at
88
+ limit $2
89
+ for update skip locked
90
+ )
91
+ update x_jobs j
92
+ set state = 'running',
93
+ attempt = j.attempt + 1,
94
+ claimed_by = $3,
95
+ visible_at = now() + ($4::bigint * interval '1 millisecond'),
96
+ updated_at = now()
97
+ from claimed c
98
+ where j.id = c.id
99
+ returning j.id, j.name, j.queue, j.input, j.idempotency_key, j.run_id, j.attempt,
100
+ j.max_attempts, j.state, j.tenant_id, j.last_error, j.claimed_by,
101
+ (extract(epoch from j.run_at) * 1000)::bigint as run_at,
102
+ (extract(epoch from j.visible_at) * 1000)::bigint as visible_at,
103
+ (extract(epoch from j.created_at) * 1000)::bigint as created_at,
104
+ (extract(epoch from j.updated_at) * 1000)::bigint as updated_at
105
+ `.trim();
106
+
107
+ export const SQL_ACK = `
108
+ update x_jobs
109
+ set state = 'done', visible_at = null, claimed_by = null, updated_at = now()
110
+ where id = $1
111
+ `.trim();
112
+
113
+ export const SQL_NACK = `
114
+ update x_jobs
115
+ set state = $2,
116
+ attempt = case when $3::boolean then attempt else greatest(attempt - 1, 0) end,
117
+ run_at = now() + ($4::bigint * interval '1 millisecond'),
118
+ visible_at = null,
119
+ claimed_by = null,
120
+ last_error = coalesce($5, last_error),
121
+ updated_at = now()
122
+ where id = $1
123
+ `.trim();
124
+
125
+ export const SQL_HEARTBEAT = `
126
+ update x_jobs
127
+ set visible_at = now() + ($2::bigint * interval '1 millisecond'), updated_at = now()
128
+ where id = $1 and state = 'running'
129
+ `.trim();
130
+
131
+ export const SQL_STATS = `
132
+ select queue,
133
+ count(*) filter (where state = 'ready' and run_at <= now()) as ready,
134
+ count(*) filter (where state = 'delayed' or run_at > now()) as delayed,
135
+ count(*) filter (where state = 'running') as running,
136
+ count(*) filter (where state = 'suspended') as suspended,
137
+ count(*) filter (where state = 'dead') as dead,
138
+ coalesce(max(extract(epoch from now() - run_at)) filter
139
+ (where state = 'ready' and run_at <= now()), 0) * 1000 as oldest_ready_ms
140
+ from x_jobs
141
+ group by queue
142
+ order by queue
143
+ `.trim();
144
+
145
+ /** Scheduler leader election. Session-scoped, so a crashed node's lock releases itself. */
146
+ export const SQL_TRY_ADVISORY_LOCK = 'select pg_try_advisory_lock($1) as locked';
147
+ export const SQL_ADVISORY_UNLOCK = 'select pg_advisory_unlock($1) as unlocked';
148
+
149
+ export const SQL_STEP_GET = `
150
+ select run_id, name, status, output, attempts, error,
151
+ (extract(epoch from started_at) * 1000)::bigint as started_at,
152
+ (extract(epoch from completed_at) * 1000)::bigint as completed_at,
153
+ (extract(epoch from wake_at) * 1000)::bigint as wake_at,
154
+ event, correlation_key
155
+ from x_job_steps where run_id = $1 and name = $2
156
+ `.trim();
157
+
158
+ export const SQL_STEP_PUT = `
159
+ insert into x_job_steps
160
+ (run_id, name, status, output, started_at, completed_at, wake_at, event,
161
+ correlation_key, attempts, error)
162
+ values ($1, $2, $3, $4::jsonb, to_timestamp($5 / 1000.0),
163
+ case when $6::bigint is null then null else to_timestamp($6 / 1000.0) end,
164
+ case when $7::bigint is null then null else to_timestamp($7 / 1000.0) end,
165
+ $8, $9, $10, $11)
166
+ on conflict (run_id, name) do update
167
+ set status = excluded.status, output = excluded.output,
168
+ completed_at = excluded.completed_at, wake_at = excluded.wake_at,
169
+ attempts = excluded.attempts, error = excluded.error
170
+ `.trim();
@@ -0,0 +1,17 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,246 @@
1
+ // The DEFAULT driver: a Postgres queue. Zero infra to start — the database you already have
2
+ // is the queue. `SELECT ... FOR UPDATE SKIP LOCKED` lets N workers claim disjoint batches
3
+ // without a coordinator, and the visibility timeout (`visible_at`) makes a worker crash cost
4
+ // one lease instead of one job. The statements themselves live in `driver-pg-sql.ts`.
5
+ import { systemClock, uuid } from '@ultimat3/core';
6
+ import { nowMs } from './clock';
7
+ import { DEFAULT_QUEUE } from './driver';
8
+ import { SQL_ACK, SQL_ADVISORY_UNLOCK, SQL_CLAIM, SQL_ENQUEUE, SQL_FIND_LIVE_BY_KEY, SQL_HEARTBEAT, SQL_NACK, SQL_STATS, SQL_STEP_GET, SQL_STEP_PUT, SQL_TRY_ADVISORY_LOCK, } from './driver-pg-sql';
9
+ import { DriverUnavailableError, JobDuplicateError } from './errors';
10
+ const num = (value) => value === null || value === undefined ? 0 : Number(value);
11
+ const optionalNum = (value) => value === null || value === undefined ? undefined : Number(value);
12
+ function toJobRecord(row) {
13
+ const visibleAt = optionalNum(row.visible_at);
14
+ return {
15
+ id: row.id,
16
+ name: row.name,
17
+ queue: row.queue,
18
+ input: row.input,
19
+ idempotencyKey: row.idempotency_key,
20
+ runId: row.run_id,
21
+ attempt: row.attempt,
22
+ maxAttempts: row.max_attempts,
23
+ state: row.state,
24
+ runAt: num(row.run_at),
25
+ createdAt: num(row.created_at),
26
+ updatedAt: num(row.updated_at),
27
+ ...(row.tenant_id === null ? {} : { tenantId: row.tenant_id }),
28
+ ...(row.last_error === null ? {} : { lastError: row.last_error }),
29
+ ...(row.claimed_by === null ? {} : { claimedBy: row.claimed_by }),
30
+ ...(visibleAt === undefined ? {} : { visibleAt }),
31
+ };
32
+ }
33
+ function toStepRecord(row) {
34
+ const completedAt = optionalNum(row.completed_at);
35
+ const wakeAt = optionalNum(row.wake_at);
36
+ return {
37
+ runId: row.run_id,
38
+ name: row.name,
39
+ status: row.status,
40
+ output: row.output,
41
+ startedAt: num(row.started_at),
42
+ attempts: row.attempts,
43
+ ...(completedAt === undefined ? {} : { completedAt }),
44
+ ...(wakeAt === undefined ? {} : { wakeAt }),
45
+ ...(row.event === null ? {} : { event: row.event }),
46
+ ...(row.correlation_key === null ? {} : { correlationKey: row.correlation_key }),
47
+ ...(row.error === null ? {} : { error: row.error }),
48
+ };
49
+ }
50
+ function resolveExecutor(injected) {
51
+ if (injected !== undefined)
52
+ return injected;
53
+ throw new DriverUnavailableError({
54
+ driver: 'pg',
55
+ cause: 'no PgExecutor was provided and Bun.sql is not configured',
56
+ fix: 'set DATABASE_URL in .env then run `x db up`, or use driver: "memory" in app.config.ts',
57
+ });
58
+ }
59
+ function pgStepStore(exec) {
60
+ return {
61
+ async get(runId, name) {
62
+ const rows = await exec().query(SQL_STEP_GET, [runId, name]);
63
+ const row = rows[0];
64
+ return row === undefined ? undefined : toStepRecord(row);
65
+ },
66
+ async put(record) {
67
+ await exec().query(SQL_STEP_PUT, [
68
+ record.runId,
69
+ record.name,
70
+ record.status,
71
+ JSON.stringify(record.output ?? null),
72
+ record.startedAt,
73
+ record.completedAt ?? null,
74
+ record.wakeAt ?? null,
75
+ record.event ?? null,
76
+ record.correlationKey ?? null,
77
+ record.attempts,
78
+ record.error ?? null,
79
+ ]);
80
+ },
81
+ async list(runId) {
82
+ const rows = await exec().query(`select * from x_job_steps where run_id = $1 order by started_at`, [runId]);
83
+ return rows.map(toStepRecord);
84
+ },
85
+ async del(runId, name) {
86
+ await exec().query(`delete from x_job_steps where run_id = $1 and name = $2`, [runId, name]);
87
+ },
88
+ async clear(runId) {
89
+ await exec().query(`delete from x_job_steps where run_id = $1`, [runId]);
90
+ },
91
+ };
92
+ }
93
+ export function createPgDriver(options = {}) {
94
+ const clock = options.clock ?? systemClock;
95
+ let executor;
96
+ const exec = () => {
97
+ executor ??= resolveExecutor(options.executor);
98
+ return executor;
99
+ };
100
+ const introspect = {
101
+ async job(jobId) {
102
+ const rows = await exec().query(`select * from x_jobs where id = $1`, [jobId]);
103
+ const row = rows[0];
104
+ return row === undefined ? undefined : toJobRecord(row);
105
+ },
106
+ async list(filter = {}) {
107
+ const rows = await exec().query(`select * from x_jobs
108
+ where ($1::text is null or queue = $1)
109
+ and ($2::text is null or name = $2)
110
+ and ($3::text is null or state = $3)
111
+ order by created_at desc
112
+ limit $4`, [filter.queue ?? null, filter.name ?? null, filter.state ?? null, filter.limit ?? 100]);
113
+ return rows.map(toJobRecord);
114
+ },
115
+ async deadLetters(limit = 100) {
116
+ const rows = await exec().query(`select * from x_jobs where state = 'dead' order by updated_at desc limit $1`, [limit]);
117
+ return rows.map(toJobRecord);
118
+ },
119
+ async requeue(jobId, requeueOptions) {
120
+ if (requeueOptions?.fromStep !== undefined) {
121
+ const current = await this.job(jobId);
122
+ if (current !== undefined) {
123
+ await exec().query(`delete from x_job_steps where run_id = $1 and name = $2`, [
124
+ current.runId,
125
+ requeueOptions.fromStep,
126
+ ]);
127
+ }
128
+ }
129
+ const rows = await exec().query(`update x_jobs set state = 'ready', attempt = 0, run_at = now(), updated_at = now()
130
+ where id = $1 returning *`, [jobId]);
131
+ const row = rows[0];
132
+ if (row === undefined) {
133
+ throw new DriverUnavailableError({
134
+ driver: 'pg',
135
+ cause: `job ${jobId} does not exist`,
136
+ fix: 'x jobs list --state dead --json',
137
+ });
138
+ }
139
+ return toJobRecord(row);
140
+ },
141
+ };
142
+ return {
143
+ name: 'pg',
144
+ steps: pgStepStore(exec),
145
+ introspect,
146
+ async enqueue(request) {
147
+ const runAt = request.runAt ?? nowMs(clock);
148
+ const rows = await exec().query(SQL_ENQUEUE, [
149
+ uuid(),
150
+ request.name,
151
+ request.queue || DEFAULT_QUEUE,
152
+ JSON.stringify(request.input ?? null),
153
+ request.idempotencyKey,
154
+ request.runId ?? uuid(),
155
+ request.maxAttempts,
156
+ runAt,
157
+ request.tenantId ?? null,
158
+ ]);
159
+ const inserted = rows[0];
160
+ if (inserted !== undefined) {
161
+ return { id: inserted.id, runId: inserted.run_id, deduped: false };
162
+ }
163
+ // `do nothing` fired: a live job already owns this idempotency key.
164
+ const existing = await exec().query(SQL_FIND_LIVE_BY_KEY, [
165
+ request.idempotencyKey,
166
+ ]);
167
+ const found = existing[0];
168
+ if (found === undefined) {
169
+ throw new DriverUnavailableError({
170
+ driver: 'pg',
171
+ cause: `enqueue of "${request.name}" was rejected but no live row holds its idempotency key`,
172
+ fix: 'x db check — the x_jobs_idempotency_live_idx index is missing or stale',
173
+ });
174
+ }
175
+ if (request.onConflict === 'error') {
176
+ throw new JobDuplicateError({
177
+ job: request.name,
178
+ idempotencyKey: request.idempotencyKey,
179
+ existingId: found.id,
180
+ });
181
+ }
182
+ return { id: found.id, runId: found.run_id, deduped: true };
183
+ },
184
+ async claim(claimOptions) {
185
+ const queues = claimOptions.queues.length > 0 ? claimOptions.queues : [DEFAULT_QUEUE];
186
+ const rows = await exec().query(SQL_CLAIM, [
187
+ queues,
188
+ claimOptions.limit,
189
+ claimOptions.workerId,
190
+ claimOptions.visibilityTimeoutMs,
191
+ ]);
192
+ const at = nowMs(clock);
193
+ return rows.map((row) => {
194
+ const record = toJobRecord(row);
195
+ return {
196
+ ...record,
197
+ claimedAt: at,
198
+ visibleAt: record.visibleAt ?? at + claimOptions.visibilityTimeoutMs,
199
+ };
200
+ });
201
+ },
202
+ async ack(jobId) {
203
+ await exec().query(SQL_ACK, [jobId]);
204
+ },
205
+ async nack(jobId, nackOptions) {
206
+ const counts = nackOptions.countsAsAttempt !== false;
207
+ const state = nackOptions.deadLetter === true ? 'dead' : counts ? 'ready' : 'suspended';
208
+ await exec().query(SQL_NACK, [
209
+ jobId,
210
+ state,
211
+ counts,
212
+ nackOptions.delayMs,
213
+ nackOptions.error ?? null,
214
+ ]);
215
+ },
216
+ async heartbeat(jobId, heartbeatOptions) {
217
+ await exec().query(SQL_HEARTBEAT, [jobId, heartbeatOptions.visibilityTimeoutMs]);
218
+ },
219
+ async stats() {
220
+ const rows = await exec().query(SQL_STATS, []);
221
+ return rows.map((row) => ({
222
+ queue: row.queue,
223
+ ready: num(row.ready),
224
+ delayed: num(row.delayed),
225
+ running: num(row.running),
226
+ suspended: num(row.suspended),
227
+ dead: num(row.dead),
228
+ oldestReadyMs: Math.round(num(row.oldest_ready_ms)),
229
+ }));
230
+ },
231
+ };
232
+ }
233
+ /** Advisory-lock leader election, used by the `scheduler` role. */
234
+ export function createPgLeader(lockKey, options = {}) {
235
+ const exec = () => resolveExecutor(options.executor);
236
+ return {
237
+ async acquire() {
238
+ const rows = await exec().query(SQL_TRY_ADVISORY_LOCK, [lockKey]);
239
+ return rows[0]?.locked === true;
240
+ },
241
+ async release() {
242
+ await exec().query(SQL_ADVISORY_UNLOCK, [lockKey]);
243
+ },
244
+ };
245
+ }
246
+ //# sourceMappingURL=driver-pg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver-pg.js","sourceRoot":"","sources":["driver-pg.ts"],"names":[],"mappings":"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"}