@bitclaw/jobs 1.1.0 → 1.3.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/queue.d.ts +13 -0
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +78 -8
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +18 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +2 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +18 -17
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { JobQueue } from './queue';
|
|
|
4
4
|
export { SlidingWindowRateLimiter } from './rate-limiter';
|
|
5
5
|
export { Scheduler } from './scheduler';
|
|
6
6
|
export { applyPragmas, initializeSchema } from './schema';
|
|
7
|
-
export type { AddJobOptions, AddScheduleOptions, BatchOptions, FailedJob, Job, JobBatch, JobContext, JobMap, JobStats, JobStatus, ListJobsOptions, PaginatedResult, PurgeOptions, RateLimit, Schedule, WorkerOptions } from './types';
|
|
7
|
+
export type { AddJobOptions, AddScheduleOptions, BackoffConfig, BatchOptions, FailedJob, Job, JobBatch, JobContext, JobMap, JobStats, JobStatus, ListJobsOptions, PaginatedResult, PurgeOptions, RateLimit, Schedule, WorkerOptions } from './types';
|
|
8
8
|
export { NonRetryableError } from './types';
|
|
9
9
|
export { JobWorker } from './worker';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,UAAU,EACV,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,eAAe,EACf,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,aAAa,EACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,UAAU,EACV,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,eAAe,EACf,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,aAAa,EACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/queue.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { JobWorker } from './worker';
|
|
|
4
4
|
export declare class JobQueue<TMap extends JobMap = Record<string, unknown>> {
|
|
5
5
|
readonly db: Database;
|
|
6
6
|
private readonly insertJobStmt;
|
|
7
|
+
private readonly selectDedupedJobStmt;
|
|
7
8
|
private readonly insertDepStmt;
|
|
8
9
|
private readonly selectJobStmt;
|
|
9
10
|
private readonly selectPendingStmt;
|
|
@@ -56,6 +57,18 @@ export declare class JobQueue<TMap extends JobMap = Record<string, unknown>> {
|
|
|
56
57
|
type: K;
|
|
57
58
|
}): JobWorker<TMap, K>;
|
|
58
59
|
private insertJob;
|
|
60
|
+
/**
|
|
61
|
+
* Reset stuck `processing` jobs back to `pending`. Call at startup to recover
|
|
62
|
+
* from server crashes that left jobs claimed but never completed.
|
|
63
|
+
* @param thresholdMs Jobs processing longer than this (ms) are reset. Default 5min.
|
|
64
|
+
* @returns Number of jobs reset.
|
|
65
|
+
*/
|
|
66
|
+
reconcileStaleJobs(thresholdMs?: number): number;
|
|
67
|
+
/**
|
|
68
|
+
* Look up a pending or processing job by its uniqueKey.
|
|
69
|
+
* Returns null if no such job exists (completed, dead-lettered, or never queued).
|
|
70
|
+
*/
|
|
71
|
+
getJobByUniqueKey(type: string, uniqueKey: string): Job | null;
|
|
59
72
|
close(): void;
|
|
60
73
|
private unblockDependents;
|
|
61
74
|
private handleBatchJobComplete;
|
package/dist/queue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,OAAO,KAAK,EACV,aAAa,
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/queue.js
CHANGED
|
@@ -59,6 +59,7 @@ function toFailedJob(row) {
|
|
|
59
59
|
export class JobQueue {
|
|
60
60
|
db;
|
|
61
61
|
insertJobStmt;
|
|
62
|
+
selectDedupedJobStmt;
|
|
62
63
|
insertDepStmt;
|
|
63
64
|
selectJobStmt;
|
|
64
65
|
selectPendingStmt;
|
|
@@ -88,8 +89,14 @@ export class JobQueue {
|
|
|
88
89
|
applyPragmas(this.db);
|
|
89
90
|
initializeSchema(this.db);
|
|
90
91
|
this.insertJobStmt = this.db.query(`
|
|
91
|
-
INSERT INTO jobs (type, data, status, priority, max_retries, run_at, batch_id)
|
|
92
|
-
VALUES ($type, $data, $status, $priority, $maxRetries, $runAt, $batchId)
|
|
92
|
+
INSERT OR IGNORE INTO jobs (type, data, status, priority, max_retries, run_at, batch_id, unique_key, backoff_config)
|
|
93
|
+
VALUES ($type, $data, $status, $priority, $maxRetries, $runAt, $batchId, $uniqueKey, $backoffConfig)
|
|
94
|
+
`);
|
|
95
|
+
this.selectDedupedJobStmt = this.db.query(`
|
|
96
|
+
SELECT id FROM jobs
|
|
97
|
+
WHERE type = $type AND unique_key = $uniqueKey
|
|
98
|
+
AND status IN ('pending', 'processing')
|
|
99
|
+
LIMIT 1
|
|
93
100
|
`);
|
|
94
101
|
this.insertDepStmt = this.db.query(`
|
|
95
102
|
INSERT INTO job_dependencies (job_id, depends_on_id) VALUES ($jobId, $depsOnId)
|
|
@@ -114,6 +121,7 @@ export class JobQueue {
|
|
|
114
121
|
SET status = 'pending',
|
|
115
122
|
retry_count = retry_count + 1,
|
|
116
123
|
error = $error,
|
|
124
|
+
run_at = $runAt,
|
|
117
125
|
updated_at = $now
|
|
118
126
|
WHERE id = $id
|
|
119
127
|
`);
|
|
@@ -279,7 +287,9 @@ export class JobQueue {
|
|
|
279
287
|
$priority: 0,
|
|
280
288
|
$maxRetries: row.max_retries,
|
|
281
289
|
$runAt: now,
|
|
282
|
-
$batchId: null
|
|
290
|
+
$batchId: null,
|
|
291
|
+
$uniqueKey: null,
|
|
292
|
+
$backoffConfig: null
|
|
283
293
|
});
|
|
284
294
|
const newJobId = this.lastInsertRowIdStmt.get();
|
|
285
295
|
this.db
|
|
@@ -382,7 +392,22 @@ export class JobQueue {
|
|
|
382
392
|
}
|
|
383
393
|
}
|
|
384
394
|
else {
|
|
385
|
-
|
|
395
|
+
const backoff = row.backoff_config
|
|
396
|
+
? JSON.parse(row.backoff_config)
|
|
397
|
+
: null;
|
|
398
|
+
let retryRunAt = now;
|
|
399
|
+
if (backoff) {
|
|
400
|
+
const delayMs = backoff.type === 'exponential'
|
|
401
|
+
? Math.min(backoff.delayMs * 2 ** row.retry_count, 3_600_000)
|
|
402
|
+
: backoff.delayMs;
|
|
403
|
+
retryRunAt = new Date(Date.now() + delayMs).toISOString();
|
|
404
|
+
}
|
|
405
|
+
this.markFailedStmt.run({
|
|
406
|
+
$id: id,
|
|
407
|
+
$error: error,
|
|
408
|
+
$runAt: retryRunAt,
|
|
409
|
+
$now: now
|
|
410
|
+
});
|
|
386
411
|
}
|
|
387
412
|
})();
|
|
388
413
|
}
|
|
@@ -430,15 +455,26 @@ export class JobQueue {
|
|
|
430
455
|
const runAt = options?.runAt ? options.runAt.toISOString() : now;
|
|
431
456
|
const hasDeps = options?.dependsOn && options.dependsOn.length > 0;
|
|
432
457
|
const status = hasDeps ? 'blocked' : 'pending';
|
|
433
|
-
this.insertJobStmt.run({
|
|
458
|
+
const result = this.insertJobStmt.run({
|
|
434
459
|
$type: type,
|
|
435
460
|
$data: JSON.stringify(data),
|
|
436
461
|
$status: status,
|
|
437
462
|
$priority: options?.priority ?? 0,
|
|
438
463
|
$maxRetries: options?.maxRetries ?? 3,
|
|
439
464
|
$runAt: runAt,
|
|
440
|
-
$batchId: batchId
|
|
465
|
+
$batchId: batchId,
|
|
466
|
+
$uniqueKey: options?.uniqueKey ?? null,
|
|
467
|
+
$backoffConfig: options?.backoff ? JSON.stringify(options.backoff) : null
|
|
441
468
|
});
|
|
469
|
+
// INSERT OR IGNORE: if a pending/processing job with same (type, uniqueKey)
|
|
470
|
+
// already exists, the insert is a no-op. Return the existing job id.
|
|
471
|
+
if (result.changes === 0 && options?.uniqueKey) {
|
|
472
|
+
const existing = this.selectDedupedJobStmt.get({
|
|
473
|
+
$type: type,
|
|
474
|
+
$uniqueKey: options.uniqueKey
|
|
475
|
+
});
|
|
476
|
+
return existing?.id ?? 0;
|
|
477
|
+
}
|
|
442
478
|
const jobId = this.lastInsertRowIdStmt.get();
|
|
443
479
|
if (hasDeps) {
|
|
444
480
|
for (const depId of options.dependsOn) {
|
|
@@ -454,6 +490,36 @@ export class JobQueue {
|
|
|
454
490
|
}
|
|
455
491
|
return jobId.id;
|
|
456
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* Reset stuck `processing` jobs back to `pending`. Call at startup to recover
|
|
495
|
+
* from server crashes that left jobs claimed but never completed.
|
|
496
|
+
* @param thresholdMs Jobs processing longer than this (ms) are reset. Default 5min.
|
|
497
|
+
* @returns Number of jobs reset.
|
|
498
|
+
*/
|
|
499
|
+
reconcileStaleJobs(thresholdMs = 300_000) {
|
|
500
|
+
const cutoff = new Date(Date.now() - thresholdMs).toISOString();
|
|
501
|
+
const result = this.db
|
|
502
|
+
.query(`UPDATE jobs
|
|
503
|
+
SET status = 'pending',
|
|
504
|
+
error = 'stale: worker crash or restart — reset for retry',
|
|
505
|
+
updated_at = $now
|
|
506
|
+
WHERE status = 'processing' AND updated_at < $cutoff`)
|
|
507
|
+
.run({ $now: nowISO(), $cutoff: cutoff });
|
|
508
|
+
return result.changes;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Look up a pending or processing job by its uniqueKey.
|
|
512
|
+
* Returns null if no such job exists (completed, dead-lettered, or never queued).
|
|
513
|
+
*/
|
|
514
|
+
getJobByUniqueKey(type, uniqueKey) {
|
|
515
|
+
const row = this.db
|
|
516
|
+
.query(`SELECT * FROM jobs
|
|
517
|
+
WHERE type = $type AND unique_key = $uniqueKey
|
|
518
|
+
AND status IN ('pending', 'processing')
|
|
519
|
+
LIMIT 1`)
|
|
520
|
+
.get({ $type: type, $uniqueKey: uniqueKey });
|
|
521
|
+
return row ? toJob(row) : null;
|
|
522
|
+
}
|
|
457
523
|
close() {
|
|
458
524
|
try {
|
|
459
525
|
if (this.db.filename !== ':memory:' && this.db.filename !== '') {
|
|
@@ -503,7 +569,9 @@ export class JobQueue {
|
|
|
503
569
|
$priority: 0,
|
|
504
570
|
$maxRetries: 3,
|
|
505
571
|
$runAt: now,
|
|
506
|
-
$batchId: null
|
|
572
|
+
$batchId: null,
|
|
573
|
+
$uniqueKey: null,
|
|
574
|
+
$backoffConfig: null
|
|
507
575
|
});
|
|
508
576
|
}
|
|
509
577
|
// Enqueue "finally" callback job regardless of failures
|
|
@@ -515,7 +583,9 @@ export class JobQueue {
|
|
|
515
583
|
$priority: 0,
|
|
516
584
|
$maxRetries: 3,
|
|
517
585
|
$runAt: now,
|
|
518
|
-
$batchId: null
|
|
586
|
+
$batchId: null,
|
|
587
|
+
$uniqueKey: null,
|
|
588
|
+
$backoffConfig: null
|
|
519
589
|
});
|
|
520
590
|
}
|
|
521
591
|
}
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoG3C,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAY/C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAsBnD"}
|
package/dist/schema.js
CHANGED
|
@@ -16,8 +16,16 @@ CREATE TABLE IF NOT EXISTS jobs (
|
|
|
16
16
|
error TEXT,
|
|
17
17
|
request_log TEXT,
|
|
18
18
|
response_log TEXT,
|
|
19
|
-
batch_id TEXT REFERENCES job_batches(id)
|
|
19
|
+
batch_id TEXT REFERENCES job_batches(id),
|
|
20
|
+
unique_key TEXT,
|
|
21
|
+
backoff_config TEXT
|
|
20
22
|
)`;
|
|
23
|
+
// State-aware dedup: same (type, unique_key) cannot be both pending/processing at once.
|
|
24
|
+
// Once the job completes, the same key can be re-enqueued.
|
|
25
|
+
const JOBS_UNIQUE_KEY_INDEX = `
|
|
26
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_jobs_unique_key
|
|
27
|
+
ON jobs (type, unique_key)
|
|
28
|
+
WHERE unique_key IS NOT NULL AND status IN ('pending', 'processing')`;
|
|
21
29
|
const JOBS_POLL_INDEX = `
|
|
22
30
|
CREATE INDEX IF NOT EXISTS idx_jobs_poll
|
|
23
31
|
ON jobs (status, type, run_at, priority DESC, created_at ASC)`;
|
|
@@ -100,4 +108,13 @@ export function initializeSchema(db) {
|
|
|
100
108
|
db.run(FAILED_JOBS_TABLE);
|
|
101
109
|
db.run(SCHEDULES_TABLE);
|
|
102
110
|
db.run(SCHEDULES_NEXT_RUN_INDEX);
|
|
111
|
+
// Migrations for columns added after initial schema creation
|
|
112
|
+
const cols = db.prepare('PRAGMA table_info(jobs)').all();
|
|
113
|
+
if (!cols.some(c => c.name === 'unique_key')) {
|
|
114
|
+
db.run('ALTER TABLE jobs ADD COLUMN unique_key TEXT');
|
|
115
|
+
}
|
|
116
|
+
if (!cols.some(c => c.name === 'backoff_config')) {
|
|
117
|
+
db.run('ALTER TABLE jobs ADD COLUMN backoff_config TEXT');
|
|
118
|
+
}
|
|
119
|
+
db.run(JOBS_UNIQUE_KEY_INDEX);
|
|
103
120
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -41,11 +41,27 @@ export type FailedJob = {
|
|
|
41
41
|
readonly requestLog: string | null;
|
|
42
42
|
readonly responseLog: string | null;
|
|
43
43
|
};
|
|
44
|
+
export type BackoffConfig = {
|
|
45
|
+
type: 'exponential' | 'fixed';
|
|
46
|
+
/** Base delay in ms. Exponential: delayMs * 2^retryCount. Fixed: always delayMs. Max 1h. */
|
|
47
|
+
delayMs: number;
|
|
48
|
+
};
|
|
44
49
|
export type AddJobOptions = {
|
|
45
50
|
priority?: number;
|
|
46
51
|
runAt?: Date;
|
|
47
52
|
maxRetries?: number;
|
|
48
53
|
dependsOn?: number[];
|
|
54
|
+
/**
|
|
55
|
+
* Deduplication key scoped to job type. If a job with the same (type, uniqueKey)
|
|
56
|
+
* is already pending or processing, the new enqueue is silently ignored and the
|
|
57
|
+
* existing job id is returned. Once the job completes, the same key can be re-used.
|
|
58
|
+
*/
|
|
59
|
+
uniqueKey?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Backoff strategy for retries. Exponential: delayMs * 2^retryCount, capped at 1h.
|
|
62
|
+
* Fixed: always delayMs between retries. Default: retry immediately.
|
|
63
|
+
*/
|
|
64
|
+
backoff?: BackoffConfig;
|
|
49
65
|
};
|
|
50
66
|
export type JobContext = {
|
|
51
67
|
reportProgress: (percent: number) => void;
|
|
@@ -63,6 +79,8 @@ export type WorkerOptions<T = unknown> = {
|
|
|
63
79
|
onError?: (job: Job<T>, error: unknown) => void;
|
|
64
80
|
/** Hard wall-clock limit per job execution in ms. Job is marked failed on timeout. */
|
|
65
81
|
timeoutMs?: number;
|
|
82
|
+
/** Max concurrent jobs this worker runs simultaneously. Default: 1. */
|
|
83
|
+
concurrency?: number;
|
|
66
84
|
};
|
|
67
85
|
export type JobStats = {
|
|
68
86
|
pending: number;
|
|
@@ -106,6 +124,8 @@ export type JobRow = {
|
|
|
106
124
|
batch_id: string | null;
|
|
107
125
|
request_log: string | null;
|
|
108
126
|
response_log: string | null;
|
|
127
|
+
unique_key: string | null;
|
|
128
|
+
backoff_config: string | null;
|
|
109
129
|
};
|
|
110
130
|
export type FailedJobRow = {
|
|
111
131
|
id: number;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,cAAc,QAAQ;gBAEnB,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,YAAY,GACZ,MAAM,GACN,QAAQ,GACR,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,OAAO,IAAI;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare class JobWorker<TMap extends JobMap = Record<string, unknown>, K
|
|
|
7
7
|
private abortController;
|
|
8
8
|
private timer;
|
|
9
9
|
private running;
|
|
10
|
-
private
|
|
10
|
+
private activeCount;
|
|
11
11
|
private stopResolve;
|
|
12
12
|
constructor(queue: JobQueue<TMap>, options: WorkerOptions<TMap[K]> & {
|
|
13
13
|
type: K;
|
|
@@ -17,5 +17,6 @@ export declare class JobWorker<TMap extends JobMap = Record<string, unknown>, K
|
|
|
17
17
|
stop(): Promise<void>;
|
|
18
18
|
private scheduleNext;
|
|
19
19
|
private poll;
|
|
20
|
+
private runJob;
|
|
20
21
|
}
|
|
21
22
|
//# sourceMappingURL=worker.d.ts.map
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,KAAK,EAAmB,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGtE,qBAAa,SAAS,CACpB,IAAI,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,IAAI;IAEnD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,KAAK,EAAmB,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGtE,qBAAa,SAAS,CACpB,IAAI,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,IAAI;IAEnD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAA6B;gBAG9C,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,CAAC,CAAA;KAAE;IAY/C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,KAAK,IAAI,IAAI;IAOP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,OAAO,CAAC,YAAY;YAMN,IAAI;YAoBJ,MAAM;CAiDrB"}
|
package/dist/worker.js
CHANGED
|
@@ -7,7 +7,7 @@ export class JobWorker {
|
|
|
7
7
|
abortController = null;
|
|
8
8
|
timer = null;
|
|
9
9
|
running = false;
|
|
10
|
-
|
|
10
|
+
activeCount = 0;
|
|
11
11
|
stopResolve = null;
|
|
12
12
|
constructor(queue, options) {
|
|
13
13
|
this.queue = queue;
|
|
@@ -35,7 +35,7 @@ export class JobWorker {
|
|
|
35
35
|
this.timer = null;
|
|
36
36
|
}
|
|
37
37
|
this.abortController?.abort();
|
|
38
|
-
if (this.
|
|
38
|
+
if (this.activeCount > 0) {
|
|
39
39
|
return new Promise(resolve => {
|
|
40
40
|
this.stopResolve = resolve;
|
|
41
41
|
});
|
|
@@ -50,16 +50,21 @@ export class JobWorker {
|
|
|
50
50
|
async poll() {
|
|
51
51
|
if (!this.running)
|
|
52
52
|
return;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
const concurrency = this.options.concurrency ?? 1;
|
|
54
|
+
// Drain available jobs up to available capacity in one poll tick
|
|
55
|
+
while (this.activeCount < concurrency) {
|
|
56
|
+
if (this.rateLimiter && !this.rateLimiter.canProceed())
|
|
57
|
+
break;
|
|
58
|
+
const job = this.queue.pollAndClaim(this.options.type);
|
|
59
|
+
if (!job)
|
|
60
|
+
break;
|
|
61
|
+
this.rateLimiter?.record();
|
|
62
|
+
this.activeCount++;
|
|
63
|
+
void this.runJob(job);
|
|
61
64
|
}
|
|
62
|
-
this.
|
|
65
|
+
this.scheduleNext();
|
|
66
|
+
}
|
|
67
|
+
async runJob(job) {
|
|
63
68
|
try {
|
|
64
69
|
const ctx = {
|
|
65
70
|
reportProgress: (percent) => {
|
|
@@ -67,7 +72,6 @@ export class JobWorker {
|
|
|
67
72
|
},
|
|
68
73
|
signal: this.abortController.signal
|
|
69
74
|
};
|
|
70
|
-
this.rateLimiter?.record();
|
|
71
75
|
const handlerPromise = this.options.handler(job, ctx);
|
|
72
76
|
if (this.options.timeoutMs) {
|
|
73
77
|
const timeoutMs = this.options.timeoutMs;
|
|
@@ -98,14 +102,11 @@ export class JobWorker {
|
|
|
98
102
|
this.options.onError?.(job, error);
|
|
99
103
|
}
|
|
100
104
|
finally {
|
|
101
|
-
this.
|
|
102
|
-
if (this.stopResolve) {
|
|
105
|
+
this.activeCount--;
|
|
106
|
+
if (!this.running && this.activeCount === 0 && this.stopResolve) {
|
|
103
107
|
this.stopResolve();
|
|
104
108
|
this.stopResolve = null;
|
|
105
109
|
}
|
|
106
|
-
else {
|
|
107
|
-
this.scheduleNext();
|
|
108
|
-
}
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
112
|
}
|