@boringnode/queue 0.5.1 → 0.6.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 +103 -20
- package/build/chunk-6IO4P6RB.js +145 -0
- package/build/chunk-6IO4P6RB.js.map +1 -0
- package/build/{chunk-VHN3XZDC.js → chunk-AHUVTAI7.js} +278 -29
- package/build/chunk-AHUVTAI7.js.map +1 -0
- package/build/chunk-S37X3CBO.js +500 -0
- package/build/chunk-S37X3CBO.js.map +1 -0
- package/build/index.d.ts +34 -8
- package/build/index.js +187 -31
- package/build/index.js.map +1 -1
- package/build/{job-DImdhRFO.d.ts → job-C4oyCVxR.d.ts} +275 -15
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +12 -6
- package/build/src/drivers/fake_adapter.js +1 -1
- package/build/src/drivers/knex_adapter.d.ts +6 -5
- package/build/src/drivers/knex_adapter.js +112 -0
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +6 -5
- package/build/src/drivers/redis_adapter.js +166 -402
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/redis_job_storage.d.ts +17 -0
- package/build/src/drivers/redis_job_storage.js +14 -0
- package/build/src/drivers/redis_job_storage.js.map +1 -0
- package/build/src/drivers/redis_scripts.d.ts +87 -0
- package/build/src/drivers/redis_scripts.js +29 -0
- package/build/src/drivers/redis_scripts.js.map +1 -0
- package/build/src/drivers/sync_adapter.d.ts +2 -1
- package/build/src/drivers/sync_adapter.js +7 -1
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/otel.d.ts +2 -2
- package/build/src/otel.js +3 -0
- package/build/src/otel.js.map +1 -1
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +7 -1
- package/package.json +18 -19
- package/build/chunk-VHN3XZDC.js.map +0 -1
|
@@ -194,6 +194,15 @@ var KnexAdapter = class {
|
|
|
194
194
|
const priority = jobData.priority ?? DEFAULT_PRIORITY;
|
|
195
195
|
const timestamp = Date.now();
|
|
196
196
|
const score = calculateScore(priority, timestamp);
|
|
197
|
+
if (jobData.dedup) {
|
|
198
|
+
return this.#pushWithDedup(queue, jobData, {
|
|
199
|
+
id: jobData.id,
|
|
200
|
+
queue,
|
|
201
|
+
status: "pending",
|
|
202
|
+
data: JSON.stringify(jobData),
|
|
203
|
+
score
|
|
204
|
+
});
|
|
205
|
+
}
|
|
197
206
|
await this.#connection(this.#jobsTable).insert({
|
|
198
207
|
id: jobData.id,
|
|
199
208
|
queue,
|
|
@@ -207,6 +216,15 @@ var KnexAdapter = class {
|
|
|
207
216
|
}
|
|
208
217
|
async pushLaterOn(queue, jobData, delay) {
|
|
209
218
|
const executeAt = Date.now() + delay;
|
|
219
|
+
if (jobData.dedup) {
|
|
220
|
+
return this.#pushWithDedup(queue, jobData, {
|
|
221
|
+
id: jobData.id,
|
|
222
|
+
queue,
|
|
223
|
+
status: "delayed",
|
|
224
|
+
data: JSON.stringify(jobData),
|
|
225
|
+
execute_at: executeAt
|
|
226
|
+
});
|
|
227
|
+
}
|
|
210
228
|
await this.#connection(this.#jobsTable).insert({
|
|
211
229
|
id: jobData.id,
|
|
212
230
|
queue,
|
|
@@ -215,11 +233,97 @@ var KnexAdapter = class {
|
|
|
215
233
|
execute_at: executeAt
|
|
216
234
|
});
|
|
217
235
|
}
|
|
236
|
+
async #pushWithDedup(queue, jobData, insertRow) {
|
|
237
|
+
const dedup = jobData.dedup;
|
|
238
|
+
try {
|
|
239
|
+
return await this.#pushWithDedupTxn(queue, jobData, insertRow, dedup);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
if (this.#isMissingDedupColumn(err)) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Dedup columns missing on "${this.#jobsTable}". Run QueueSchemaService.addDedupColumns() on your jobs table before dispatching jobs with .dedup().`,
|
|
244
|
+
{ cause: err }
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
throw err;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
#isMissingDedupColumn(err) {
|
|
251
|
+
if (!err || typeof err !== "object") return false;
|
|
252
|
+
const message = err.message;
|
|
253
|
+
if (!message) return false;
|
|
254
|
+
return /dedup_id/.test(message) && /(does not exist|no such column|Unknown column)/i.test(message);
|
|
255
|
+
}
|
|
256
|
+
#pushWithDedupTxn(queue, jobData, insertRow, dedup) {
|
|
257
|
+
return this.#connection.transaction(async (trx) => {
|
|
258
|
+
const existing = await trx(this.#jobsTable).where("queue", queue).where("dedup_id", dedup.id).orderBy("dedup_at", "desc").forUpdate().first();
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
if (existing) {
|
|
261
|
+
const dedupAt = existing.dedup_at != null ? Number(existing.dedup_at) : null;
|
|
262
|
+
const dedupTtl = existing.dedup_ttl != null ? Number(existing.dedup_ttl) : null;
|
|
263
|
+
const withinTtl = dedupTtl === null || dedupAt !== null && now - dedupAt < dedupTtl;
|
|
264
|
+
if (withinTtl) {
|
|
265
|
+
const status2 = existing.status;
|
|
266
|
+
const replaceable = status2 === "pending" || status2 === "delayed";
|
|
267
|
+
if (dedup.replace && replaceable) {
|
|
268
|
+
const storedData = typeof existing.data === "string" ? JSON.parse(existing.data) : existing.data;
|
|
269
|
+
const newData = { ...storedData, payload: jobData.payload };
|
|
270
|
+
const updates = { data: JSON.stringify(newData) };
|
|
271
|
+
if (dedup.extend && dedupTtl) {
|
|
272
|
+
updates.dedup_at = now;
|
|
273
|
+
}
|
|
274
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update(updates);
|
|
275
|
+
return { outcome: "replaced", jobId: existing.id };
|
|
276
|
+
}
|
|
277
|
+
if (dedup.extend && dedupTtl) {
|
|
278
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_at: now });
|
|
279
|
+
return { outcome: "extended", jobId: existing.id };
|
|
280
|
+
}
|
|
281
|
+
return { outcome: "skipped", jobId: existing.id };
|
|
282
|
+
}
|
|
283
|
+
const status = existing.status;
|
|
284
|
+
if (status === "pending" || status === "delayed" || status === "active") {
|
|
285
|
+
await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_id: null, dedup_at: null, dedup_ttl: null });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let raceLost = false;
|
|
289
|
+
try {
|
|
290
|
+
await trx.transaction(async (sp) => {
|
|
291
|
+
await sp(this.#jobsTable).insert({
|
|
292
|
+
...insertRow,
|
|
293
|
+
dedup_id: dedup.id,
|
|
294
|
+
dedup_at: now,
|
|
295
|
+
dedup_ttl: dedup.ttl ?? null
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
} catch (err) {
|
|
299
|
+
if (this.#isUniqueViolation(err)) {
|
|
300
|
+
raceLost = true;
|
|
301
|
+
} else {
|
|
302
|
+
throw err;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (raceLost) {
|
|
306
|
+
const winner = await trx(this.#jobsTable).where("queue", queue).where("dedup_id", dedup.id).whereIn("status", ["pending", "delayed"]).orderBy("dedup_at", "desc").first();
|
|
307
|
+
if (winner) {
|
|
308
|
+
return { outcome: "skipped", jobId: winner.id };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { outcome: "added", jobId: jobData.id };
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
#isUniqueViolation(err) {
|
|
315
|
+
if (!err || typeof err !== "object") return false;
|
|
316
|
+
const e = err;
|
|
317
|
+
return e.code === "23505" || e.code === "SQLITE_CONSTRAINT_UNIQUE" || /UNIQUE constraint/i.test(e.message ?? "");
|
|
318
|
+
}
|
|
218
319
|
async pushMany(jobs) {
|
|
219
320
|
return this.pushManyOn("default", jobs);
|
|
220
321
|
}
|
|
221
322
|
async pushManyOn(queue, jobs) {
|
|
222
323
|
if (jobs.length === 0) return;
|
|
324
|
+
if (jobs.some((j) => j.dedup)) {
|
|
325
|
+
throw new Error("dedup is not supported in batch dispatch; use single dispatch");
|
|
326
|
+
}
|
|
223
327
|
const now = Date.now();
|
|
224
328
|
const rows = jobs.map((job) => ({
|
|
225
329
|
id: job.id,
|
|
@@ -269,6 +373,14 @@ var KnexAdapter = class {
|
|
|
269
373
|
return recovered;
|
|
270
374
|
});
|
|
271
375
|
}
|
|
376
|
+
async renewJobs(queue, jobIds) {
|
|
377
|
+
if (jobIds.length === 0) {
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
const now = Date.now();
|
|
381
|
+
const renewed = await this.#connection(this.#jobsTable).where("queue", queue).where("status", "active").where("worker_id", this.#workerId).whereIn("id", jobIds).update({ acquired_at: now });
|
|
382
|
+
return renewed;
|
|
383
|
+
}
|
|
272
384
|
async upsertSchedule(config) {
|
|
273
385
|
const id = config.id ?? randomUUID();
|
|
274
386
|
const data = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/drivers/knex_adapter.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport KnexPkg from 'knex'\nimport type { Knex } from 'knex'\nimport type { Adapter, AcquiredJob } from '../contracts/adapter.js'\nimport type {\n JobData,\n JobRecord,\n JobRetention,\n JobStatus,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { calculateScore, resolveRetention } from '../utils.js'\n\nexport interface KnexAdapterOptions {\n connection: Knex\n tableName?: string\n schedulesTableName?: string\n ownsConnection?: boolean\n}\n\ntype KnexConfig = Knex | Knex.Config\ntype DbRow = Record<string, unknown>\n\ninterface ScheduleRow extends DbRow {\n id: string\n name: string\n payload: unknown\n cron_expression: string | null\n every_ms: number | string | null\n timezone: string | null\n from_date: Date | string | number | null\n to_date: Date | string | number | null\n run_limit: number | string | null\n run_count: number | string | null\n next_run_at: Date | string | number | null\n last_run_at: Date | string | number | null\n status: string\n created_at: Date | string | number | null\n}\n\n/**\n * Create a new Knex adapter factory.\n * Accepts either a Knex instance or a Knex configuration object.\n *\n * When passing a config object, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Knex instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function knex(config: KnexConfig, tableName?: string) {\n return () => {\n const isKnexInstance = typeof config === 'function'\n const connection = isKnexInstance ? config : KnexPkg(config)\n return new KnexAdapter({ connection, tableName, ownsConnection: !isKnexInstance })\n }\n}\n\n/**\n * Knex adapter for the queue system.\n * Stores jobs in a SQL database using Knex.\n */\nexport class KnexAdapter implements Adapter {\n readonly #connection: Knex\n readonly #jobsTable: string\n readonly #schedulesTable: string\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(config: KnexAdapterOptions) {\n this.#connection = config.connection\n this.#jobsTable = config.tableName ?? 'queue_jobs'\n this.#schedulesTable = config.schedulesTableName ?? 'queue_schedules'\n this.#ownsConnection = config.ownsConnection ?? false\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.destroy()\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n\n // First, move ready delayed jobs to pending\n await this.#processDelayedJobs(queue, now)\n\n // Use a transaction to atomically pop a job\n return this.#connection.transaction(async (trx) => {\n // Build the query for highest priority job (lowest score)\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .orderBy('score', 'asc')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const job = await query.first()\n\n if (!job) {\n return null\n }\n\n // Update job to active status\n // For SQLite (no SKIP LOCKED), add status='pending' guard to prevent double-claim\n const updateQuery = trx(this.#jobsTable)\n .where('id', job.id)\n .where('queue', queue)\n\n if (!this.#supportsSkipLocked()) {\n updateQuery.where('status', 'pending')\n }\n\n const updated = await updateQuery.update({\n status: 'active',\n worker_id: this.#workerId,\n acquired_at: now,\n })\n\n // Another worker already claimed this job\n if (updated === 0) {\n return null\n }\n\n const jobData: JobData = JSON.parse(job.data)\n\n return {\n ...jobData,\n acquiredAt: now,\n }\n })\n }\n\n /**\n * Check if the database supports FOR UPDATE SKIP LOCKED.\n * PostgreSQL 9.5+, MySQL 8.0+, and MariaDB 10.6+ support it.\n * SQLite does not, but it's single-writer so it doesn't need it.\n */\n #supportsSkipLocked(): boolean {\n const client = this.#connection.client.config.client\n return client === 'pg' || client === 'mysql' || client === 'mysql2' || client === 'mariadb'\n }\n\n async #processDelayedJobs(queue: string, now: number): Promise<void> {\n // Use a transaction with row locking to prevent race conditions\n await this.#connection.transaction(async (trx) => {\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'delayed')\n .where('execute_at', '<=', now)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const delayedJobs = await query\n\n if (delayedJobs.length === 0) return\n\n // Move them to pending\n for (const job of delayedJobs) {\n const jobData: JobData = JSON.parse(job.data)\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', job.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n score,\n execute_at: null,\n })\n }\n })\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnComplete)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'completed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'completed', maxAge, maxCount, now)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnFail)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'failed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n error: error?.message || null,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'failed', maxAge, maxCount, now)\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const row = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .first()\n\n if (!row) {\n return null\n }\n\n const jobData: JobData = JSON.parse(row.data)\n\n return {\n status: row.status as JobStatus,\n data: jobData,\n finishedAt: row.finished_at ? Number(row.finished_at) : undefined,\n error: row.error || undefined,\n }\n }\n\n async #pruneHistory(\n queue: string,\n status: 'completed' | 'failed',\n maxAge: number,\n maxCount: number,\n now: number\n ): Promise<void> {\n if (maxAge > 0) {\n const cutoff = now - maxAge\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .where('finished_at', '<', cutoff)\n .delete()\n }\n\n if (maxCount > 0) {\n const toKeep = this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .orderBy('finished_at', 'desc')\n .limit(maxCount)\n .select('id')\n\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .whereNotIn('id', toKeep)\n .delete()\n }\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n\n // Get the active job\n const activeJob = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .first()\n\n if (!activeJob) return\n\n const jobData: JobData = JSON.parse(activeJob.data)\n jobData.attempts = (jobData.attempts || 0) + 1\n\n const updatedData = JSON.stringify(jobData)\n\n if (retryAt && retryAt.getTime() > now) {\n // Move to delayed\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .update({\n status: 'delayed',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score: null,\n execute_at: retryAt.getTime(),\n })\n } else {\n // Move back to pending\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score,\n execute_at: null,\n })\n }\n }\n\n async push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const timestamp = Date.now()\n const score = calculateScore(priority, timestamp)\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void> {\n const executeAt = Date.now() + delay\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n if (jobs.length === 0) return\n\n const now = Date.now()\n const rows = jobs.map((job) => ({\n id: job.id,\n queue,\n status: 'pending' as const,\n data: JSON.stringify(job),\n score: calculateScore(job.priority ?? DEFAULT_PRIORITY, now),\n }))\n\n await this.#connection(this.#jobsTable).insert(rows)\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const result = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .count('* as count')\n .first()\n\n return Number(result?.count ?? 0)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n const stalledCutoff = now - stalledThreshold\n\n // Use a transaction with row locking to prevent race conditions\n return this.#connection.transaction(async (trx) => {\n let recovered = 0\n\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('acquired_at', '<', stalledCutoff)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const stalledJobs = await query\n\n for (const row of stalledJobs) {\n const jobData: JobData = JSON.parse(row.data)\n const currentStalledCount = jobData.stalledCount ?? 0\n\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - remove the job\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .delete()\n } else {\n // Recover: increment stalledCount and put back in pending\n jobData.stalledCount = currentStalledCount + 1\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: JSON.stringify(jobData),\n worker_id: null,\n acquired_at: null,\n score,\n })\n\n recovered++\n }\n }\n\n return recovered\n })\n }\n\n async upsertSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n\n const data = {\n id,\n name: config.name,\n payload: JSON.stringify(config.payload),\n cron_expression: config.cronExpression ?? null,\n every_ms: config.everyMs ?? null,\n timezone: config.timezone,\n from_date: config.from ?? null,\n to_date: config.to ?? null,\n run_limit: config.limit ?? null,\n status: 'active',\n }\n\n // Atomic upsert\n await this.#connection(this.#schedulesTable)\n .insert({\n ...data,\n run_count: 0,\n created_at: this.#connection.fn.now(),\n })\n .onConflict('id')\n .merge({\n name: data.name,\n payload: data.payload,\n cron_expression: data.cron_expression,\n every_ms: data.every_ms,\n timezone: data.timezone,\n from_date: data.from_date,\n to_date: data.to_date,\n run_limit: data.run_limit,\n status: 'active',\n })\n\n return id\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n const row = (await this.#connection(this.#schedulesTable)\n .where('id', id)\n .first()) as ScheduleRow | undefined\n if (!row) return null\n\n return this.#rowToScheduleData(row)\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n let query = this.#connection(this.#schedulesTable).whereNot('status', 'cancelled')\n\n if (options?.status) {\n query = query.where('status', options.status)\n }\n\n const rows = (await query) as ScheduleRow[]\n return rows.map((row) => this.#rowToScheduleData(row))\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const data: Record<string, unknown> = {}\n\n if (updates.status !== undefined) data.status = updates.status\n if (updates.nextRunAt !== undefined) data.next_run_at = updates.nextRunAt\n if (updates.lastRunAt !== undefined) data.last_run_at = updates.lastRunAt\n if (updates.runCount !== undefined) data.run_count = updates.runCount\n\n if (Object.keys(data).length > 0) {\n await this.#connection(this.#schedulesTable)\n .where('id', id)\n .update(data)\n }\n }\n\n async deleteSchedule(id: string): Promise<void> {\n await this.#connection(this.#schedulesTable)\n .where('id', id)\n .delete()\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n return this.#connection.transaction(async (trx) => {\n // Find one due schedule with row locking\n let query = trx(this.#schedulesTable)\n .where('status', 'active')\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', now)\n .where((builder) => {\n builder.whereNull('run_limit').orWhereRaw('run_count < run_limit')\n })\n .where((builder) => {\n builder.whereNull('to_date').orWhere('to_date', '>=', now)\n })\n .orderBy('next_run_at', 'asc')\n .limit(1)\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const row = (await query.first()) as ScheduleRow | undefined\n if (!row) return null\n\n // Calculate next run time\n let nextRunAt: Date | null = null\n const newRunCount = Number(row.run_count ?? 0) + 1\n\n if (row.every_ms) {\n nextRunAt = new Date(now.getTime() + Number(row.every_ms))\n } else if (row.cron_expression) {\n // Import cron-parser dynamically to calculate next run\n const { CronExpressionParser } = await import('cron-parser')\n const cron = CronExpressionParser.parse(row.cron_expression, {\n currentDate: now,\n tz: row.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached\n if (row.run_limit !== null && newRunCount >= Number(row.run_limit)) {\n nextRunAt = null\n }\n\n // Check if past end date\n if (nextRunAt && row.to_date && nextRunAt > new Date(row.to_date)) {\n nextRunAt = null\n }\n\n // Update atomically\n await trx(this.#schedulesTable)\n .where('id', row.id)\n .update({\n next_run_at: nextRunAt,\n last_run_at: now,\n run_count: newRunCount,\n })\n\n // Return schedule data (before update state for payload)\n return this.#rowToScheduleData(row)\n })\n }\n\n #rowToScheduleData(row: ScheduleRow): ScheduleData {\n return {\n id: row.id,\n name: row.name,\n payload: typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload,\n cronExpression: row.cron_expression ?? null,\n everyMs: row.every_ms ? Number(row.every_ms) : null,\n timezone: row.timezone ?? 'UTC',\n from: row.from_date ? new Date(row.from_date) : null,\n to: row.to_date ? new Date(row.to_date) : null,\n limit: row.run_limit ? Number(row.run_limit) : null,\n runCount: Number(row.run_count ?? 0),\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : null,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : null,\n status: row.status === 'paused' || row.status === 'cancelled' ? 'paused' : 'active',\n createdAt: row.created_at ? new Date(row.created_at) : new Date(),\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/drivers/knex_adapter.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport KnexPkg from 'knex'\nimport type { Knex } from 'knex'\nimport type { Adapter, AcquiredJob, PushResult } from '../contracts/adapter.js'\nimport type {\n DedupOutcome,\n JobData,\n JobRecord,\n JobRetention,\n JobStatus,\n ScheduleConfig,\n ScheduleData,\n ScheduleListOptions,\n} from '../types/main.js'\nimport { DEFAULT_PRIORITY } from '../constants.js'\nimport { calculateScore, resolveRetention } from '../utils.js'\n\nexport interface KnexAdapterOptions {\n connection: Knex\n tableName?: string\n schedulesTableName?: string\n ownsConnection?: boolean\n}\n\ntype KnexConfig = Knex | Knex.Config\ntype DbRow = Record<string, unknown>\n\ninterface ScheduleRow extends DbRow {\n id: string\n name: string\n payload: unknown\n cron_expression: string | null\n every_ms: number | string | null\n timezone: string | null\n from_date: Date | string | number | null\n to_date: Date | string | number | null\n run_limit: number | string | null\n run_count: number | string | null\n next_run_at: Date | string | number | null\n last_run_at: Date | string | number | null\n status: string\n created_at: Date | string | number | null\n}\n\n/**\n * Create a new Knex adapter factory.\n * Accepts either a Knex instance or a Knex configuration object.\n *\n * When passing a config object, the adapter will create and manage\n * the connection lifecycle (closing it on destroy).\n *\n * When passing a Knex instance, the caller is responsible for\n * managing the connection lifecycle.\n */\nexport function knex(config: KnexConfig, tableName?: string) {\n return () => {\n const isKnexInstance = typeof config === 'function'\n const connection = isKnexInstance ? config : KnexPkg(config)\n return new KnexAdapter({ connection, tableName, ownsConnection: !isKnexInstance })\n }\n}\n\n/**\n * Knex adapter for the queue system.\n * Stores jobs in a SQL database using Knex.\n */\nexport class KnexAdapter implements Adapter {\n readonly #connection: Knex\n readonly #jobsTable: string\n readonly #schedulesTable: string\n readonly #ownsConnection: boolean\n #workerId: string = ''\n\n constructor(config: KnexAdapterOptions) {\n this.#connection = config.connection\n this.#jobsTable = config.tableName ?? 'queue_jobs'\n this.#schedulesTable = config.schedulesTableName ?? 'queue_schedules'\n this.#ownsConnection = config.ownsConnection ?? false\n }\n\n setWorkerId(workerId: string): void {\n this.#workerId = workerId\n }\n\n async destroy(): Promise<void> {\n if (this.#ownsConnection) {\n await this.#connection.destroy()\n }\n }\n\n async pop(): Promise<AcquiredJob | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<AcquiredJob | null> {\n const now = Date.now()\n\n // First, move ready delayed jobs to pending\n await this.#processDelayedJobs(queue, now)\n\n // Use a transaction to atomically pop a job\n return this.#connection.transaction(async (trx) => {\n // Build the query for highest priority job (lowest score)\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .orderBy('score', 'asc')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const job = await query.first()\n\n if (!job) {\n return null\n }\n\n // Update job to active status\n // For SQLite (no SKIP LOCKED), add status='pending' guard to prevent double-claim\n const updateQuery = trx(this.#jobsTable).where('id', job.id).where('queue', queue)\n\n if (!this.#supportsSkipLocked()) {\n updateQuery.where('status', 'pending')\n }\n\n const updated = await updateQuery.update({\n status: 'active',\n worker_id: this.#workerId,\n acquired_at: now,\n })\n\n // Another worker already claimed this job\n if (updated === 0) {\n return null\n }\n\n const jobData: JobData = JSON.parse(job.data)\n\n return {\n ...jobData,\n acquiredAt: now,\n }\n })\n }\n\n /**\n * Check if the database supports FOR UPDATE SKIP LOCKED.\n * PostgreSQL 9.5+, MySQL 8.0+, and MariaDB 10.6+ support it.\n * SQLite does not, but it's single-writer so it doesn't need it.\n */\n #supportsSkipLocked(): boolean {\n const client = this.#connection.client.config.client\n return client === 'pg' || client === 'mysql' || client === 'mysql2' || client === 'mariadb'\n }\n\n async #processDelayedJobs(queue: string, now: number): Promise<void> {\n // Use a transaction with row locking to prevent race conditions\n await this.#connection.transaction(async (trx) => {\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'delayed')\n .where('execute_at', '<=', now)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const delayedJobs = await query\n\n if (delayedJobs.length === 0) return\n\n // Move them to pending\n for (const job of delayedJobs) {\n const jobData: JobData = JSON.parse(job.data)\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable).where('id', job.id).where('queue', queue).update({\n status: 'pending',\n score,\n execute_at: null,\n })\n }\n })\n }\n\n async completeJob(jobId: string, queue: string, removeOnComplete?: JobRetention): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnComplete)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'completed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'completed', maxAge, maxCount, now)\n }\n\n async failJob(\n jobId: string,\n queue: string,\n error?: Error,\n removeOnFail?: JobRetention\n ): Promise<void> {\n const { keep, maxAge, maxCount } = resolveRetention(removeOnFail)\n\n if (!keep) {\n await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .delete()\n return\n }\n\n const now = Date.now()\n\n const updated = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .update({\n status: 'failed',\n worker_id: null,\n acquired_at: null,\n finished_at: now,\n error: error?.message || null,\n })\n\n if (!updated) {\n return\n }\n\n await this.#pruneHistory(queue, 'failed', maxAge, maxCount, now)\n }\n\n async getJob(jobId: string, queue: string): Promise<JobRecord | null> {\n const row = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .first()\n\n if (!row) {\n return null\n }\n\n const jobData: JobData = JSON.parse(row.data)\n\n return {\n status: row.status as JobStatus,\n data: jobData,\n finishedAt: row.finished_at ? Number(row.finished_at) : undefined,\n error: row.error || undefined,\n }\n }\n\n async #pruneHistory(\n queue: string,\n status: 'completed' | 'failed',\n maxAge: number,\n maxCount: number,\n now: number\n ): Promise<void> {\n if (maxAge > 0) {\n const cutoff = now - maxAge\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .where('finished_at', '<', cutoff)\n .delete()\n }\n\n if (maxCount > 0) {\n const toKeep = this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .orderBy('finished_at', 'desc')\n .limit(maxCount)\n .select('id')\n\n await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', status)\n .whereNotIn('id', toKeep)\n .delete()\n }\n }\n\n async retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void> {\n const now = Date.now()\n\n // Get the active job\n const activeJob = await this.#connection(this.#jobsTable)\n .where('id', jobId)\n .where('queue', queue)\n .where('status', 'active')\n .first()\n\n if (!activeJob) return\n\n const jobData: JobData = JSON.parse(activeJob.data)\n jobData.attempts = (jobData.attempts || 0) + 1\n\n const updatedData = JSON.stringify(jobData)\n\n if (retryAt && retryAt.getTime() > now) {\n // Move to delayed\n await this.#connection(this.#jobsTable).where('id', jobId).where('queue', queue).update({\n status: 'delayed',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score: null,\n execute_at: retryAt.getTime(),\n })\n } else {\n // Move back to pending\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await this.#connection(this.#jobsTable).where('id', jobId).where('queue', queue).update({\n status: 'pending',\n data: updatedData,\n worker_id: null,\n acquired_at: null,\n score,\n execute_at: null,\n })\n }\n }\n\n async push(jobData: JobData): Promise<PushResult | void> {\n return this.pushOn('default', jobData)\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<PushResult | void> {\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const timestamp = Date.now()\n const score = calculateScore(priority, timestamp)\n\n if (jobData.dedup) {\n return this.#pushWithDedup(queue, jobData, {\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'pending',\n data: JSON.stringify(jobData),\n score,\n })\n }\n\n async pushLater(jobData: JobData, delay: number): Promise<PushResult | void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n async pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void> {\n const executeAt = Date.now() + delay\n\n if (jobData.dedup) {\n return this.#pushWithDedup(queue, jobData, {\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n await this.#connection(this.#jobsTable).insert({\n id: jobData.id,\n queue,\n status: 'delayed',\n data: JSON.stringify(jobData),\n execute_at: executeAt,\n })\n }\n\n async #pushWithDedup(\n queue: string,\n jobData: JobData,\n insertRow: Record<string, unknown>\n ): Promise<PushResult> {\n const dedup = jobData.dedup!\n\n try {\n return await this.#pushWithDedupTxn(queue, jobData, insertRow, dedup)\n } catch (err) {\n if (this.#isMissingDedupColumn(err)) {\n throw new Error(\n `Dedup columns missing on \"${this.#jobsTable}\". Run QueueSchemaService.addDedupColumns() on your jobs table before dispatching jobs with .dedup().`,\n { cause: err }\n )\n }\n throw err\n }\n }\n\n #isMissingDedupColumn(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const message = (err as { message?: string }).message\n if (!message) return false\n // Postgres: 'column \"dedup_id\" does not exist'\n // SQLite: 'no such column: dedup_id'\n // MySQL: \"Unknown column 'dedup_id' in 'where clause'\"\n return (\n /dedup_id/.test(message) && /(does not exist|no such column|Unknown column)/i.test(message)\n )\n }\n\n #pushWithDedupTxn(\n queue: string,\n jobData: JobData,\n insertRow: Record<string, unknown>,\n dedup: NonNullable<JobData['dedup']>\n ): Promise<PushResult> {\n return this.#connection.transaction(async (trx) => {\n const existing = await trx(this.#jobsTable)\n .where('queue', queue)\n .where('dedup_id', dedup.id)\n .orderBy('dedup_at', 'desc')\n .forUpdate()\n .first()\n\n const now = Date.now()\n\n if (existing) {\n const dedupAt = existing.dedup_at != null ? Number(existing.dedup_at) : null\n const dedupTtl = existing.dedup_ttl != null ? Number(existing.dedup_ttl) : null\n const withinTtl = dedupTtl === null || (dedupAt !== null && now - dedupAt < dedupTtl)\n\n if (withinTtl) {\n const status = existing.status as JobStatus\n const replaceable = status === 'pending' || status === 'delayed'\n\n if (dedup.replace && replaceable) {\n const storedData =\n typeof existing.data === 'string' ? JSON.parse(existing.data) : existing.data\n const newData = { ...storedData, payload: jobData.payload }\n const updates: Record<string, unknown> = { data: JSON.stringify(newData) }\n if (dedup.extend && dedupTtl) {\n updates.dedup_at = now\n }\n await trx(this.#jobsTable).where({ id: existing.id, queue }).update(updates)\n return { outcome: 'replaced' as DedupOutcome, jobId: existing.id as string }\n }\n\n if (dedup.extend && dedupTtl) {\n await trx(this.#jobsTable).where({ id: existing.id, queue }).update({ dedup_at: now })\n return { outcome: 'extended' as DedupOutcome, jobId: existing.id as string }\n }\n\n return { outcome: 'skipped' as DedupOutcome, jobId: existing.id as string }\n }\n // TTL expired — release the dedup slot from the old row so the new\n // insert can claim it. The old job keeps running to completion; only\n // its dedup identity is cleared. Retained history rows are excluded\n // from the partial unique index predicate, so no update needed there.\n const status = existing.status as JobStatus\n if (status === 'pending' || status === 'delayed' || status === 'active') {\n await trx(this.#jobsTable)\n .where({ id: existing.id, queue })\n .update({ dedup_id: null, dedup_at: null, dedup_ttl: null })\n }\n }\n\n let raceLost = false\n try {\n await trx.transaction(async (sp) => {\n await sp(this.#jobsTable).insert({\n ...insertRow,\n dedup_id: dedup.id,\n dedup_at: now,\n dedup_ttl: dedup.ttl ?? null,\n })\n })\n } catch (err) {\n if (this.#isUniqueViolation(err)) {\n raceLost = true\n } else {\n throw err\n }\n }\n\n if (raceLost) {\n const winner = await trx(this.#jobsTable)\n .where('queue', queue)\n .where('dedup_id', dedup.id)\n .whereIn('status', ['pending', 'delayed'])\n .orderBy('dedup_at', 'desc')\n .first()\n if (winner) {\n return { outcome: 'skipped' as DedupOutcome, jobId: winner.id as string }\n }\n }\n\n return { outcome: 'added' as DedupOutcome, jobId: jobData.id }\n })\n }\n\n #isUniqueViolation(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false\n const e = err as { code?: string; message?: string }\n return (\n e.code === '23505' ||\n e.code === 'SQLITE_CONSTRAINT_UNIQUE' ||\n /UNIQUE constraint/i.test(e.message ?? '')\n )\n }\n\n async pushMany(jobs: JobData[]): Promise<void> {\n return this.pushManyOn('default', jobs)\n }\n\n async pushManyOn(queue: string, jobs: JobData[]): Promise<void> {\n if (jobs.length === 0) return\n\n if (jobs.some((j) => j.dedup)) {\n throw new Error('dedup is not supported in batch dispatch; use single dispatch')\n }\n\n const now = Date.now()\n const rows = jobs.map((job) => ({\n id: job.id,\n queue,\n status: 'pending' as const,\n data: JSON.stringify(job),\n score: calculateScore(job.priority ?? DEFAULT_PRIORITY, now),\n }))\n\n await this.#connection(this.#jobsTable).insert(rows)\n }\n\n async size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n async sizeOf(queue: string): Promise<number> {\n const result = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'pending')\n .count('* as count')\n .first()\n\n return Number(result?.count ?? 0)\n }\n\n async recoverStalledJobs(\n queue: string,\n stalledThreshold: number,\n maxStalledCount: number\n ): Promise<number> {\n const now = Date.now()\n const stalledCutoff = now - stalledThreshold\n\n // Use a transaction with row locking to prevent race conditions\n return this.#connection.transaction(async (trx) => {\n let recovered = 0\n\n let query = trx(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('acquired_at', '<', stalledCutoff)\n .select('id', 'data')\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const stalledJobs = await query\n\n for (const row of stalledJobs) {\n const jobData: JobData = JSON.parse(row.data)\n const currentStalledCount = jobData.stalledCount ?? 0\n\n if (currentStalledCount >= maxStalledCount) {\n // Fail permanently - remove the job\n await trx(this.#jobsTable).where('id', row.id).where('queue', queue).delete()\n } else {\n // Recover: increment stalledCount and put back in pending\n jobData.stalledCount = currentStalledCount + 1\n const priority = jobData.priority ?? DEFAULT_PRIORITY\n const score = calculateScore(priority, now)\n\n await trx(this.#jobsTable)\n .where('id', row.id)\n .where('queue', queue)\n .update({\n status: 'pending',\n data: JSON.stringify(jobData),\n worker_id: null,\n acquired_at: null,\n score,\n })\n\n recovered++\n }\n }\n\n return recovered\n })\n }\n\n async renewJobs(queue: string, jobIds: string[]): Promise<number> {\n if (jobIds.length === 0) {\n return 0\n }\n\n const now = Date.now()\n\n // Only renew jobs that are still active AND still owned by this worker; a\n // job that was already recovered, finalized, or re-acquired by another\n // worker will not match and is therefore never resurrected.\n const renewed = await this.#connection(this.#jobsTable)\n .where('queue', queue)\n .where('status', 'active')\n .where('worker_id', this.#workerId)\n .whereIn('id', jobIds)\n .update({ acquired_at: now })\n\n return renewed\n }\n\n async upsertSchedule(config: ScheduleConfig): Promise<string> {\n const id = config.id ?? randomUUID()\n\n const data = {\n id,\n name: config.name,\n payload: JSON.stringify(config.payload),\n cron_expression: config.cronExpression ?? null,\n every_ms: config.everyMs ?? null,\n timezone: config.timezone,\n from_date: config.from ?? null,\n to_date: config.to ?? null,\n run_limit: config.limit ?? null,\n status: 'active',\n }\n\n // Atomic upsert\n await this.#connection(this.#schedulesTable)\n .insert({\n ...data,\n run_count: 0,\n created_at: this.#connection.fn.now(),\n })\n .onConflict('id')\n .merge({\n name: data.name,\n payload: data.payload,\n cron_expression: data.cron_expression,\n every_ms: data.every_ms,\n timezone: data.timezone,\n from_date: data.from_date,\n to_date: data.to_date,\n run_limit: data.run_limit,\n status: 'active',\n })\n\n return id\n }\n\n /**\n * @deprecated Use `upsertSchedule` instead.\n */\n createSchedule(config: ScheduleConfig): Promise<string> {\n return this.upsertSchedule(config)\n }\n\n async getSchedule(id: string): Promise<ScheduleData | null> {\n const row = (await this.#connection(this.#schedulesTable).where('id', id).first()) as\n | ScheduleRow\n | undefined\n if (!row) return null\n\n return this.#rowToScheduleData(row)\n }\n\n async listSchedules(options?: ScheduleListOptions): Promise<ScheduleData[]> {\n let query = this.#connection(this.#schedulesTable).whereNot('status', 'cancelled')\n\n if (options?.status) {\n query = query.where('status', options.status)\n }\n\n const rows = (await query) as ScheduleRow[]\n return rows.map((row) => this.#rowToScheduleData(row))\n }\n\n async updateSchedule(\n id: string,\n updates: Partial<Pick<ScheduleData, 'status' | 'nextRunAt' | 'lastRunAt' | 'runCount'>>\n ): Promise<void> {\n const data: Record<string, unknown> = {}\n\n if (updates.status !== undefined) data.status = updates.status\n if (updates.nextRunAt !== undefined) data.next_run_at = updates.nextRunAt\n if (updates.lastRunAt !== undefined) data.last_run_at = updates.lastRunAt\n if (updates.runCount !== undefined) data.run_count = updates.runCount\n\n if (Object.keys(data).length > 0) {\n await this.#connection(this.#schedulesTable).where('id', id).update(data)\n }\n }\n\n async deleteSchedule(id: string): Promise<void> {\n await this.#connection(this.#schedulesTable).where('id', id).delete()\n }\n\n async claimDueSchedule(): Promise<ScheduleData | null> {\n const now = new Date()\n\n return this.#connection.transaction(async (trx) => {\n // Find one due schedule with row locking\n let query = trx(this.#schedulesTable)\n .where('status', 'active')\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', now)\n .where((builder) => {\n builder.whereNull('run_limit').orWhereRaw('run_count < run_limit')\n })\n .where((builder) => {\n builder.whereNull('to_date').orWhere('to_date', '>=', now)\n })\n .orderBy('next_run_at', 'asc')\n .limit(1)\n\n if (this.#supportsSkipLocked()) {\n query = query.forUpdate().skipLocked()\n }\n\n const row = (await query.first()) as ScheduleRow | undefined\n if (!row) return null\n\n // Calculate next run time\n let nextRunAt: Date | null = null\n const newRunCount = Number(row.run_count ?? 0) + 1\n\n if (row.every_ms) {\n nextRunAt = new Date(now.getTime() + Number(row.every_ms))\n } else if (row.cron_expression) {\n // Import cron-parser dynamically to calculate next run\n const { CronExpressionParser } = await import('cron-parser')\n const cron = CronExpressionParser.parse(row.cron_expression, {\n currentDate: now,\n tz: row.timezone || 'UTC',\n })\n nextRunAt = cron.next().toDate()\n }\n\n // Check if limit will be reached\n if (row.run_limit !== null && newRunCount >= Number(row.run_limit)) {\n nextRunAt = null\n }\n\n // Check if past end date\n if (nextRunAt && row.to_date && nextRunAt > new Date(row.to_date)) {\n nextRunAt = null\n }\n\n // Update atomically\n await trx(this.#schedulesTable).where('id', row.id).update({\n next_run_at: nextRunAt,\n last_run_at: now,\n run_count: newRunCount,\n })\n\n // Return schedule data (before update state for payload)\n return this.#rowToScheduleData(row)\n })\n }\n\n #rowToScheduleData(row: ScheduleRow): ScheduleData {\n return {\n id: row.id,\n name: row.name,\n payload: typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload,\n cronExpression: row.cron_expression ?? null,\n everyMs: row.every_ms ? Number(row.every_ms) : null,\n timezone: row.timezone ?? 'UTC',\n from: row.from_date ? new Date(row.from_date) : null,\n to: row.to_date ? new Date(row.to_date) : null,\n limit: row.run_limit ? Number(row.run_limit) : null,\n runCount: Number(row.run_count ?? 0),\n nextRunAt: row.next_run_at ? new Date(row.next_run_at) : null,\n lastRunAt: row.last_run_at ? new Date(row.last_run_at) : null,\n status: row.status === 'paused' || row.status === 'cancelled' ? 'paused' : 'active',\n createdAt: row.created_at ? new Date(row.created_at) : new Date(),\n }\n }\n}\n"],"mappings":"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","names":["status"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Redis, RedisOptions } from 'ioredis';
|
|
2
|
-
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-
|
|
2
|
+
import { A as Adapter, b as AcquiredJob, c as JobRetention, d as JobRecord, J as JobData, P as PushResult, S as ScheduleConfig, e as ScheduleData, f as ScheduleListOptions } from '../../job-C4oyCVxR.js';
|
|
3
3
|
|
|
4
4
|
type RedisConfig = Redis | RedisOptions;
|
|
5
5
|
/**
|
|
@@ -24,15 +24,16 @@ declare class RedisAdapter implements Adapter {
|
|
|
24
24
|
failJob(jobId: string, queue: string, error?: Error, removeOnFail?: JobRetention): Promise<void>;
|
|
25
25
|
retryJob(jobId: string, queue: string, retryAt?: Date): Promise<void>;
|
|
26
26
|
getJob(jobId: string, queue: string): Promise<JobRecord | null>;
|
|
27
|
-
push(jobData: JobData): Promise<void>;
|
|
28
|
-
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
29
|
-
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
30
|
-
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
27
|
+
push(jobData: JobData): Promise<PushResult | void>;
|
|
28
|
+
pushLater(jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
29
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<PushResult | void>;
|
|
30
|
+
pushOn(queue: string, jobData: JobData): Promise<PushResult | void>;
|
|
31
31
|
pushMany(jobs: JobData[]): Promise<void>;
|
|
32
32
|
pushManyOn(queue: string, jobs: JobData[]): Promise<void>;
|
|
33
33
|
size(): Promise<number>;
|
|
34
34
|
sizeOf(queue: string): Promise<number>;
|
|
35
35
|
recoverStalledJobs(queue: string, stalledThreshold: number, maxStalledCount: number): Promise<number>;
|
|
36
|
+
renewJobs(queue: string, jobIds: string[]): Promise<number>;
|
|
36
37
|
upsertSchedule(config: ScheduleConfig): Promise<string>;
|
|
37
38
|
/**
|
|
38
39
|
* @deprecated Use `upsertSchedule` instead.
|