@boringnode/queue 0.0.1-alpha
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/LICENSE.md +9 -0
- package/README.md +0 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +452 -0
- package/build/index.js.map +1 -0
- package/build/job-CcAUWe8j.d.ts +125 -0
- package/build/src/contracts/adapter.d.ts +2 -0
- package/build/src/contracts/adapter.js +1 -0
- package/build/src/contracts/adapter.js.map +1 -0
- package/build/src/contracts/lease_manager.d.ts +8 -0
- package/build/src/contracts/lease_manager.js +1 -0
- package/build/src/contracts/lease_manager.js.map +1 -0
- package/build/src/drivers/redis_adapter.d.ts +22 -0
- package/build/src/drivers/redis_adapter.js +107 -0
- package/build/src/drivers/redis_adapter.js.map +1 -0
- package/build/src/drivers/sync_adapter.d.ts +19 -0
- package/build/src/drivers/sync_adapter.js +53 -0
- package/build/src/drivers/sync_adapter.js.map +1 -0
- package/build/src/types/main.d.ts +2 -0
- package/build/src/types/main.js +1 -0
- package/build/src/types/main.js.map +1 -0
- package/package.json +88 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Romain Lanz, contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
File without changes
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Q as QueueManagerConfig, W as WorkerCycle, A as Adapter, R as RetryConfig } from './job-CcAUWe8j.js';
|
|
2
|
+
export { J as Job, c as customBackoff, e as exponentialBackoff, f as fixedBackoff, l as linearBackoff } from './job-CcAUWe8j.js';
|
|
3
|
+
import './src/contracts/lease_manager.js';
|
|
4
|
+
|
|
5
|
+
declare class Worker {
|
|
6
|
+
#private;
|
|
7
|
+
get id(): string;
|
|
8
|
+
constructor(config: QueueManagerConfig);
|
|
9
|
+
init(): Promise<void>;
|
|
10
|
+
start(queues?: string[]): Promise<void>;
|
|
11
|
+
stop(): Promise<void>;
|
|
12
|
+
processCycle(queues: string[]): Promise<WorkerCycle | null>;
|
|
13
|
+
process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare class QueueManagerSingleton {
|
|
17
|
+
#private;
|
|
18
|
+
init(config: QueueManagerConfig): Promise<this>;
|
|
19
|
+
use(adapter?: string): Adapter;
|
|
20
|
+
/**
|
|
21
|
+
* Priority: job > queue > global
|
|
22
|
+
*/
|
|
23
|
+
getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig;
|
|
24
|
+
destroy(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
declare const QueueManager: QueueManagerSingleton;
|
|
27
|
+
|
|
28
|
+
export { QueueManager, Worker };
|
package/build/index.js
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
// src/job.ts
|
|
2
|
+
import { JobDispatcher } from "#src/job_dispatcher";
|
|
3
|
+
var Job = class {
|
|
4
|
+
#payload;
|
|
5
|
+
static options = {};
|
|
6
|
+
get payload() {
|
|
7
|
+
return this.#payload;
|
|
8
|
+
}
|
|
9
|
+
constructor(payload) {
|
|
10
|
+
this.#payload = payload;
|
|
11
|
+
}
|
|
12
|
+
static dispatch(payload) {
|
|
13
|
+
const dispatcher = new JobDispatcher(
|
|
14
|
+
this.jobName,
|
|
15
|
+
payload
|
|
16
|
+
);
|
|
17
|
+
if (this.options.queue) {
|
|
18
|
+
dispatcher.toQueue(this.options.queue);
|
|
19
|
+
}
|
|
20
|
+
if (this.options.adapter) {
|
|
21
|
+
dispatcher.with(this.options.adapter);
|
|
22
|
+
}
|
|
23
|
+
if (this.options.priority !== void 0) {
|
|
24
|
+
dispatcher.priority(this.options.priority);
|
|
25
|
+
}
|
|
26
|
+
return dispatcher;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/worker.ts
|
|
31
|
+
import { randomUUID } from "crypto";
|
|
32
|
+
import { setTimeout } from "timers/promises";
|
|
33
|
+
import debug from "#src/debug";
|
|
34
|
+
import { parse } from "#src/utils";
|
|
35
|
+
import * as errors from "#src/exceptions";
|
|
36
|
+
import { QueueManager } from "#src/queue_manager";
|
|
37
|
+
import { Locator } from "#src/locator";
|
|
38
|
+
var Worker = class {
|
|
39
|
+
#id;
|
|
40
|
+
#config;
|
|
41
|
+
#adapter;
|
|
42
|
+
#leaseManager;
|
|
43
|
+
#running = false;
|
|
44
|
+
#initialized = false;
|
|
45
|
+
#generator;
|
|
46
|
+
get id() {
|
|
47
|
+
return this.#id;
|
|
48
|
+
}
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.#config = config;
|
|
51
|
+
this.#id = randomUUID();
|
|
52
|
+
debug("created worker with id %s and config %O", this.#id, config);
|
|
53
|
+
}
|
|
54
|
+
async init() {
|
|
55
|
+
if (this.#initialized) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
debug("initializing worker %s", this.#id);
|
|
59
|
+
await QueueManager.init(this.#config);
|
|
60
|
+
this.#adapter = QueueManager.use();
|
|
61
|
+
this.#leaseManager = this.#adapter.createLeaseManager({
|
|
62
|
+
workerId: this.#id,
|
|
63
|
+
leaseTimeout: parse(this.#config.worker?.leaseTimeout || "5m"),
|
|
64
|
+
renewalInterval: parse(this.#config.worker?.renewalInterval || "5m")
|
|
65
|
+
});
|
|
66
|
+
this.#initialized = true;
|
|
67
|
+
debug("worker %s initialized", this.#id);
|
|
68
|
+
}
|
|
69
|
+
async start(queues = ["default"]) {
|
|
70
|
+
await this.init();
|
|
71
|
+
if (this.#running) {
|
|
72
|
+
debug("worker %s is already running", this.#id);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.#running = true;
|
|
76
|
+
debug("starting worker %s on queues: %O", this.#id, queues);
|
|
77
|
+
await this.#setupGracefulShutdown();
|
|
78
|
+
for await (const cycle of this.process(queues)) {
|
|
79
|
+
if (["started", "completed"].includes(cycle.type)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (["idle", "error"].includes(cycle.type)) {
|
|
83
|
+
const delay = parse(cycle.suggestedDelay);
|
|
84
|
+
if (cycle.type === "error") {
|
|
85
|
+
debug("worker %s encountered an error: %O", this.#id, cycle.error);
|
|
86
|
+
} else {
|
|
87
|
+
debug("worker %s is idle, waiting for %dms", this.#id, delay);
|
|
88
|
+
}
|
|
89
|
+
await setTimeout(delay);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async stop() {
|
|
94
|
+
debug("stopping worker %s", this.#id);
|
|
95
|
+
this.#running = false;
|
|
96
|
+
if (this.#leaseManager) {
|
|
97
|
+
await this.#leaseManager.destroy();
|
|
98
|
+
}
|
|
99
|
+
if (this.#adapter) {
|
|
100
|
+
await this.#adapter.destroy();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async processCycle(queues) {
|
|
104
|
+
await this.init();
|
|
105
|
+
this.#running = true;
|
|
106
|
+
if (!this.#generator) {
|
|
107
|
+
this.#generator = this.process(queues);
|
|
108
|
+
}
|
|
109
|
+
const result = await this.#generator.next();
|
|
110
|
+
if (result.done) {
|
|
111
|
+
this.#generator = void 0;
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return result.value;
|
|
115
|
+
}
|
|
116
|
+
async *process(queues) {
|
|
117
|
+
const concurrency = this.#config.worker?.concurrency || 1;
|
|
118
|
+
runningLoop: while (this.#running) {
|
|
119
|
+
try {
|
|
120
|
+
for (const queue of queues) {
|
|
121
|
+
const jobs = await this.#acquireJobs(queue, concurrency);
|
|
122
|
+
if (jobs.length > 0) {
|
|
123
|
+
for (const job of jobs) {
|
|
124
|
+
yield { type: "started", queue, job };
|
|
125
|
+
}
|
|
126
|
+
const results = await Promise.allSettled(jobs.map((job) => this.#execute(job, queue)));
|
|
127
|
+
for (const job of jobs) {
|
|
128
|
+
yield { type: "completed", queue, job };
|
|
129
|
+
}
|
|
130
|
+
const hasError = results.some((r) => r.status === "rejected");
|
|
131
|
+
if (hasError) {
|
|
132
|
+
const error = results.find((r) => r.status === "rejected");
|
|
133
|
+
yield { type: "error", error: error.reason, suggestedDelay: parse("5s") };
|
|
134
|
+
}
|
|
135
|
+
continue runningLoop;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const pollingInterval = parse(this.#config.worker?.pollingInterval || "2s");
|
|
139
|
+
yield { type: "idle", suggestedDelay: pollingInterval };
|
|
140
|
+
} catch (error) {
|
|
141
|
+
yield { type: "error", error, suggestedDelay: parse("5s") };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async #execute(job, queue) {
|
|
146
|
+
const startTime = performance.now();
|
|
147
|
+
debug("worker %s: executing job %s (%s)", this.#id, job.id, job.name);
|
|
148
|
+
let JobClass;
|
|
149
|
+
try {
|
|
150
|
+
JobClass = Locator.getOrThrow(job.name);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
debug("worker %s: job class %s not found for job %s", this.#id, job.name, job.id);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
const instance = new JobClass(job.payload);
|
|
156
|
+
const options = JobClass.options || {};
|
|
157
|
+
try {
|
|
158
|
+
await instance.execute();
|
|
159
|
+
await job._lease.commit();
|
|
160
|
+
const duration = (performance.now() - startTime).toFixed(2);
|
|
161
|
+
debug("worker %s: successfully executed job %s in %dms", this.#id, job.id, duration);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry);
|
|
164
|
+
if (typeof mergedConfig.maxRetries === "undefined" || mergedConfig.maxRetries <= 0) {
|
|
165
|
+
debug("worker %s: job %s has no retries configured, marking as failed", this.#id, job.id);
|
|
166
|
+
await instance.failed(e);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (job.attempts >= mergedConfig.maxRetries) {
|
|
170
|
+
debug(
|
|
171
|
+
"worker %s: job %s has exceeded max retries (%d), marking as failed",
|
|
172
|
+
this.#id,
|
|
173
|
+
job.id,
|
|
174
|
+
mergedConfig.maxRetries
|
|
175
|
+
);
|
|
176
|
+
const exception = new errors.E_JOB_MAX_ATTEMPTS_REACHED([job.name]);
|
|
177
|
+
await instance.failed(exception);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (mergedConfig.backoff) {
|
|
181
|
+
const strategy = mergedConfig.backoff();
|
|
182
|
+
const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1);
|
|
183
|
+
debug("worker %s: job %s will retry at %s", this.#id, job.id, nextRetryAt.toISOString());
|
|
184
|
+
await this.#rollbackJobWithBackoff(job, queue, nextRetryAt);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await this.#rollbackJob(job, queue);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async #acquireJobs(queue, count) {
|
|
191
|
+
const jobs = [];
|
|
192
|
+
for (let i = 0; i < count; i++) {
|
|
193
|
+
const job = await this.#adapter.popFrom(queue);
|
|
194
|
+
if (!job) {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
debug("worker %s: attempting to acquire lease for job %s", this.#id, job.id);
|
|
198
|
+
try {
|
|
199
|
+
const acquired = await this.#leaseManager.acquire(job.id);
|
|
200
|
+
if (!acquired) {
|
|
201
|
+
debug("worker %s: failed to acquire lease for job %s", this.#id, job.id);
|
|
202
|
+
await this.#adapter.pushOn(queue, job);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
debug("worker %s: acquired lease for job %s", this.#id, job.id);
|
|
206
|
+
jobs.push({
|
|
207
|
+
...job,
|
|
208
|
+
_lease: {
|
|
209
|
+
commit: () => this.#commitJob(job.id),
|
|
210
|
+
rollback: () => this.#rollbackJob(job, queue)
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.log(error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return jobs;
|
|
219
|
+
}
|
|
220
|
+
#commitJob(jobId) {
|
|
221
|
+
debug("worker %s: committing job %s", this.#id, jobId);
|
|
222
|
+
return this.#leaseManager.release(jobId);
|
|
223
|
+
}
|
|
224
|
+
async #rollbackJob(job, queue) {
|
|
225
|
+
debug("worker %s: rolling back job %s", this.#id, job.id);
|
|
226
|
+
const updatedJob = {
|
|
227
|
+
...job,
|
|
228
|
+
attempts: (job.attempts || 0) + 1
|
|
229
|
+
};
|
|
230
|
+
await Promise.all([this.#leaseManager.release(job.id), this.#adapter.pushOn(queue, updatedJob)]);
|
|
231
|
+
}
|
|
232
|
+
async #rollbackJobWithBackoff(job, queue, nextRetryAt) {
|
|
233
|
+
debug("worker %s: rolling back job %s with backoff", this.#id, job.id);
|
|
234
|
+
const updatedJob = {
|
|
235
|
+
...job,
|
|
236
|
+
attempts: (job.attempts || 0) + 1,
|
|
237
|
+
nextRetryAt
|
|
238
|
+
};
|
|
239
|
+
await this.#leaseManager.release(job.id);
|
|
240
|
+
const delay = nextRetryAt.getTime() - Date.now();
|
|
241
|
+
if (delay > 0) {
|
|
242
|
+
await this.#adapter.pushLaterOn(queue, updatedJob, delay);
|
|
243
|
+
} else {
|
|
244
|
+
await this.#adapter.pushOn(queue, updatedJob);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async #setupGracefulShutdown() {
|
|
248
|
+
const shutdown = async () => {
|
|
249
|
+
debug("received shutdown signal, stopping worker...");
|
|
250
|
+
await this.stop();
|
|
251
|
+
process.exit(0);
|
|
252
|
+
};
|
|
253
|
+
process.on("SIGINT", shutdown);
|
|
254
|
+
process.on("SIGTERM", shutdown);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// src/queue_manager.ts
|
|
259
|
+
import * as errors2 from "#src/exceptions";
|
|
260
|
+
import debug2 from "#src/debug";
|
|
261
|
+
import { Locator as Locator2 } from "#src/locator";
|
|
262
|
+
var QueueManagerSingleton = class {
|
|
263
|
+
#defaultAdapter;
|
|
264
|
+
#adapters = {};
|
|
265
|
+
#globalRetryConfig;
|
|
266
|
+
#queueConfigs = /* @__PURE__ */ new Map();
|
|
267
|
+
async init(config) {
|
|
268
|
+
debug2("initializing queue manager with config: %O", config);
|
|
269
|
+
this.#validateConfig(config);
|
|
270
|
+
this.#defaultAdapter = config.default;
|
|
271
|
+
this.#adapters = config.adapters;
|
|
272
|
+
this.#globalRetryConfig = config.retry;
|
|
273
|
+
if (config.queues) {
|
|
274
|
+
for (const [queue, queueConfig] of Object.entries(config.queues)) {
|
|
275
|
+
this.#queueConfigs.set(queue, queueConfig);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
await Locator2.registerFromGlob(config.locations);
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
use(adapter) {
|
|
282
|
+
if (!adapter) {
|
|
283
|
+
adapter = this.#defaultAdapter;
|
|
284
|
+
}
|
|
285
|
+
const adapterInstance = this.#adapters[adapter];
|
|
286
|
+
if (!adapterInstance) {
|
|
287
|
+
throw new errors2.E_CONFIGURATION_ERROR([`Adapter "${adapter}" is not registered`]);
|
|
288
|
+
}
|
|
289
|
+
debug2('using adapter "%s"', adapter);
|
|
290
|
+
try {
|
|
291
|
+
return adapterInstance();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
throw new Error();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Priority: job > queue > global
|
|
298
|
+
*/
|
|
299
|
+
getMergedRetryConfig(queue, jobRetryConfig) {
|
|
300
|
+
const queueConfig = this.#queueConfigs.get(queue);
|
|
301
|
+
const queueRetryConfig = queueConfig?.retry || {};
|
|
302
|
+
let maxRetries = jobRetryConfig?.maxRetries || queueRetryConfig.maxRetries || this.#globalRetryConfig?.maxRetries || 0;
|
|
303
|
+
let backoff = jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff;
|
|
304
|
+
return { maxRetries, backoff };
|
|
305
|
+
}
|
|
306
|
+
#validateConfig(config) {
|
|
307
|
+
if (!config.adapters || Object.keys(config.adapters).length === 0) {
|
|
308
|
+
throw new errors2.E_CONFIGURATION_ERROR(["At least one adapter must be configured"]);
|
|
309
|
+
}
|
|
310
|
+
if (!config.default) {
|
|
311
|
+
throw new errors2.E_CONFIGURATION_ERROR(["Default adapter must be specified"]);
|
|
312
|
+
}
|
|
313
|
+
if (!config.locations || config.locations.length === 0) {
|
|
314
|
+
throw new errors2.E_CONFIGURATION_ERROR(["Job locations must be specified"]);
|
|
315
|
+
}
|
|
316
|
+
if (!config.adapters[config.default]) {
|
|
317
|
+
throw new errors2.E_CONFIGURATION_ERROR([
|
|
318
|
+
`Default adapter "${config.default}" not found in adapters configuration`
|
|
319
|
+
]);
|
|
320
|
+
}
|
|
321
|
+
for (const [name, factory] of Object.entries(config.adapters)) {
|
|
322
|
+
if (typeof factory !== "function") {
|
|
323
|
+
throw new errors2.E_CONFIGURATION_ERROR([`Adapter "${name}" must be a factory function`]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async destroy() {
|
|
328
|
+
for (const adapterName in this.#adapters) {
|
|
329
|
+
const adapter = this.#adapters[adapterName]();
|
|
330
|
+
await adapter.destroy();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var QueueManager2 = new QueueManagerSingleton();
|
|
335
|
+
|
|
336
|
+
// src/strategies/backoff_strategy.ts
|
|
337
|
+
import * as errors3 from "#src/exceptions";
|
|
338
|
+
import { parse as parse2 } from "#src/utils";
|
|
339
|
+
import { RuntimeException } from "@poppinss/utils";
|
|
340
|
+
import { assertUnreachable } from "@poppinss/utils/assert";
|
|
341
|
+
var BackoffStrategy = class {
|
|
342
|
+
#config;
|
|
343
|
+
constructor(config) {
|
|
344
|
+
this.#config = config;
|
|
345
|
+
this.#validateConfig();
|
|
346
|
+
}
|
|
347
|
+
calculateDelay(attempt) {
|
|
348
|
+
if (attempt < 1) {
|
|
349
|
+
throw new RuntimeException("Attempt number must be >= 1");
|
|
350
|
+
}
|
|
351
|
+
const baseDelayMs = parse2(this.#config.baseDelay);
|
|
352
|
+
const maxDelayMs = this.#config.maxDelay ? parse2(this.#config.maxDelay) : Infinity;
|
|
353
|
+
const multiplier = this.#config.multiplier ?? 2;
|
|
354
|
+
let delay;
|
|
355
|
+
switch (this.#config.strategy) {
|
|
356
|
+
case "exponential":
|
|
357
|
+
delay = baseDelayMs * Math.pow(multiplier, attempt - 1);
|
|
358
|
+
break;
|
|
359
|
+
case "linear":
|
|
360
|
+
delay = baseDelayMs * attempt;
|
|
361
|
+
break;
|
|
362
|
+
case "fixed":
|
|
363
|
+
delay = baseDelayMs;
|
|
364
|
+
break;
|
|
365
|
+
default:
|
|
366
|
+
assertUnreachable(this.#config.strategy);
|
|
367
|
+
}
|
|
368
|
+
delay = Math.min(delay, maxDelayMs);
|
|
369
|
+
if (this.#config.jitter) {
|
|
370
|
+
delay = this.#applyJitter(delay);
|
|
371
|
+
}
|
|
372
|
+
return Math.floor(delay);
|
|
373
|
+
}
|
|
374
|
+
getNextRetryAt(attempt) {
|
|
375
|
+
const delay = this.calculateDelay(attempt);
|
|
376
|
+
return new Date(Date.now() + delay);
|
|
377
|
+
}
|
|
378
|
+
getConfig() {
|
|
379
|
+
return Object.freeze({ ...this.#config });
|
|
380
|
+
}
|
|
381
|
+
#validateConfig() {
|
|
382
|
+
const baseDelayMs = parse2(this.#config.baseDelay);
|
|
383
|
+
if (baseDelayMs <= 0) {
|
|
384
|
+
throw new errors3.E_INVALID_BASE_DELAY([
|
|
385
|
+
"Base delay must be a positive integer greater than zero"
|
|
386
|
+
]);
|
|
387
|
+
}
|
|
388
|
+
if (this.#config.maxDelay) {
|
|
389
|
+
const maxDelayMs = parse2(this.#config.maxDelay);
|
|
390
|
+
if (maxDelayMs <= 0) {
|
|
391
|
+
throw new errors3.E_INVALID_MAX_DELAY([
|
|
392
|
+
"Max delay must be a positive integer greater than zero"
|
|
393
|
+
]);
|
|
394
|
+
}
|
|
395
|
+
if (maxDelayMs <= baseDelayMs) {
|
|
396
|
+
throw new errors3.E_INVALID_MAX_DELAY(["Max delay should be greater than base delay"]);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (this.#config.multiplier !== void 0) {
|
|
400
|
+
if (this.#config.multiplier <= 0) {
|
|
401
|
+
throw new errors3.E_INVALID_MULTIPLIER([
|
|
402
|
+
"Multiplier must be a positive number greater than zero"
|
|
403
|
+
]);
|
|
404
|
+
}
|
|
405
|
+
if (this.#config.strategy === "exponential" && this.#config.multiplier < 1) {
|
|
406
|
+
throw new errors3.E_INVALID_MULTIPLIER(["Exponential strategy multiplier should be >= 1"]);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
#applyJitter(delay) {
|
|
411
|
+
const jitterRange = delay * 0.25;
|
|
412
|
+
const jitter = (Math.random() - 0.5) * 2 * jitterRange;
|
|
413
|
+
return Math.max(0, delay + jitter);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
function exponentialBackoff(config) {
|
|
417
|
+
return () => new BackoffStrategy({
|
|
418
|
+
strategy: "exponential",
|
|
419
|
+
baseDelay: "1s",
|
|
420
|
+
maxDelay: "5m",
|
|
421
|
+
multiplier: 2,
|
|
422
|
+
jitter: true,
|
|
423
|
+
...config
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function linearBackoff(config) {
|
|
427
|
+
return () => new BackoffStrategy({
|
|
428
|
+
strategy: "linear",
|
|
429
|
+
baseDelay: "5s",
|
|
430
|
+
maxDelay: "2m",
|
|
431
|
+
...config
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
function fixedBackoff(delay = "10s") {
|
|
435
|
+
return () => new BackoffStrategy({
|
|
436
|
+
strategy: "fixed",
|
|
437
|
+
baseDelay: delay
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function customBackoff(config) {
|
|
441
|
+
return () => new BackoffStrategy(config);
|
|
442
|
+
}
|
|
443
|
+
export {
|
|
444
|
+
Job,
|
|
445
|
+
QueueManager2 as QueueManager,
|
|
446
|
+
Worker,
|
|
447
|
+
customBackoff,
|
|
448
|
+
exponentialBackoff,
|
|
449
|
+
fixedBackoff,
|
|
450
|
+
linearBackoff
|
|
451
|
+
};
|
|
452
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/job.ts","../src/worker.ts","../src/queue_manager.ts","../src/strategies/backoff_strategy.ts"],"sourcesContent":["import { JobDispatcher } from '#src/job_dispatcher'\nimport type { JobOptions } from '#types/main'\n\nexport abstract class Job<Payload = any> {\n readonly #payload: Payload\n\n static options: JobOptions = {}\n\n get payload(): Payload {\n return this.#payload\n }\n\n constructor(payload: Payload) {\n this.#payload = payload\n }\n\n static dispatch<T extends Job>(\n this: new (payload: any) => T,\n payload: T extends Job<infer P> ? P : never\n ): JobDispatcher<T extends Job<infer P> ? P : never> {\n const dispatcher = new JobDispatcher<T extends Job<infer P> ? P : never>(\n (this as any).jobName,\n payload\n )\n\n if ((this as any).options.queue) {\n dispatcher.toQueue((this as any).options.queue)\n }\n\n if ((this as any).options.adapter) {\n dispatcher.with((this as any).options.adapter)\n }\n\n if ((this as any).options.priority !== undefined) {\n dispatcher.priority((this as any).options.priority)\n }\n\n return dispatcher\n }\n\n abstract execute(): Promise<void>\n\n failed?(error: Error): Promise<void>\n}\n","import { randomUUID } from 'node:crypto'\nimport { setTimeout } from 'node:timers/promises'\nimport debug from '#src/debug'\nimport { parse } from '#src/utils'\nimport * as errors from '#src/exceptions'\nimport { QueueManager } from '#src/queue_manager'\nimport type { Adapter } from '#contracts/adapter'\nimport type { LeaseManager } from '#contracts/lease_manager'\nimport type { AcquiredJob, JobData, QueueManagerConfig, WorkerCycle } from '#types/main'\nimport { Locator } from '#src/locator'\nimport type { JobOptions } from '#types/main'\n\nexport class Worker {\n readonly #id: string\n readonly #config: QueueManagerConfig\n #adapter!: Adapter\n #leaseManager!: LeaseManager\n #running = false\n #initialized = false\n #generator?: AsyncGenerator<WorkerCycle, void, unknown>\n\n get id() {\n return this.#id\n }\n\n constructor(config: QueueManagerConfig) {\n this.#config = config\n this.#id = randomUUID()\n\n debug('created worker with id %s and config %O', this.#id, config)\n }\n\n async init() {\n if (this.#initialized) {\n return\n }\n\n debug('initializing worker %s', this.#id)\n\n await QueueManager.init(this.#config)\n\n this.#adapter = QueueManager.use()\n this.#leaseManager = this.#adapter.createLeaseManager({\n workerId: this.#id,\n leaseTimeout: parse(this.#config.worker?.leaseTimeout || '5m'),\n renewalInterval: parse(this.#config.worker?.renewalInterval || '5m'),\n })\n\n this.#initialized = true\n\n debug('worker %s initialized', this.#id)\n }\n\n async start(queues: string[] = ['default']): Promise<void> {\n await this.init()\n\n if (this.#running) {\n debug('worker %s is already running', this.#id)\n return\n }\n\n this.#running = true\n\n debug('starting worker %s on queues: %O', this.#id, queues)\n\n await this.#setupGracefulShutdown()\n\n for await (const cycle of this.process(queues)) {\n if (['started', 'completed'].includes(cycle.type)) {\n continue\n }\n\n if (['idle', 'error'].includes(cycle.type)) {\n // @ts-expect-error - we know suggestedDelay exists for these types\n const delay = parse(cycle.suggestedDelay)\n\n if (cycle.type === 'error') {\n debug('worker %s encountered an error: %O', this.#id, cycle.error)\n } else {\n debug('worker %s is idle, waiting for %dms', this.#id, delay)\n }\n\n await setTimeout(delay)\n }\n }\n }\n\n async stop() {\n debug('stopping worker %s', this.#id)\n\n this.#running = false\n\n if (this.#leaseManager) {\n await this.#leaseManager.destroy()\n }\n\n if (this.#adapter) {\n await this.#adapter.destroy()\n }\n }\n\n async processCycle(queues: string[]): Promise<WorkerCycle | null> {\n await this.init()\n\n this.#running = true\n\n if (!this.#generator) {\n this.#generator = this.process(queues)\n }\n\n const result = await this.#generator.next()\n\n if (result.done) {\n this.#generator = undefined\n return null\n }\n\n return result.value\n }\n\n async *process(queues: string[]): AsyncGenerator<WorkerCycle, void, unknown> {\n const concurrency = this.#config.worker?.concurrency || 1\n\n runningLoop: while (this.#running) {\n try {\n for (const queue of queues) {\n const jobs = await this.#acquireJobs(queue, concurrency)\n\n if (jobs.length > 0) {\n // Yield started events for all jobs\n for (const job of jobs) {\n yield { type: 'started', queue, job }\n }\n\n // Execute all jobs in parallel\n const results = await Promise.allSettled(jobs.map((job) => this.#execute(job, queue)))\n\n // Yield completed events for all jobs\n for (const job of jobs) {\n yield { type: 'completed', queue, job: job }\n }\n\n // Check if any job failed\n const hasError = results.some((r) => r.status === 'rejected')\n if (hasError) {\n const error = results.find((r) => r.status === 'rejected') as PromiseRejectedResult\n yield { type: 'error', error: error.reason, suggestedDelay: parse('5s') }\n }\n\n continue runningLoop\n }\n }\n\n const pollingInterval = parse(this.#config.worker?.pollingInterval || '2s')\n yield { type: 'idle', suggestedDelay: pollingInterval }\n } catch (error) {\n yield { type: 'error', error: error as Error, suggestedDelay: parse('5s') }\n }\n }\n }\n\n async #execute(job: AcquiredJob, queue: string): Promise<void> {\n const startTime = performance.now()\n\n debug('worker %s: executing job %s (%s)', this.#id, job.id, job.name)\n\n let JobClass: any\n\n try {\n JobClass = Locator.getOrThrow(job.name)\n } catch (error) {\n debug('worker %s: job class %s not found for job %s', this.#id, job.name, job.id)\n throw error\n }\n\n const instance = new JobClass(job.payload)\n const options: JobOptions = JobClass.options || {}\n\n try {\n await instance.execute()\n await job._lease.commit()\n\n const duration = (performance.now() - startTime).toFixed(2)\n debug('worker %s: successfully executed job %s in %dms', this.#id, job.id, duration)\n } catch (e) {\n const mergedConfig = QueueManager.getMergedRetryConfig(queue, options.retry)\n\n if (typeof mergedConfig.maxRetries === 'undefined' || mergedConfig.maxRetries <= 0) {\n debug('worker %s: job %s has no retries configured, marking as failed', this.#id, job.id)\n\n await instance.failed(e as Error)\n return\n }\n\n if (job.attempts >= mergedConfig.maxRetries!) {\n debug(\n 'worker %s: job %s has exceeded max retries (%d), marking as failed',\n this.#id,\n job.id,\n mergedConfig.maxRetries\n )\n\n const exception = new errors.E_JOB_MAX_ATTEMPTS_REACHED([job.name])\n await instance.failed(exception)\n\n return\n }\n\n if (mergedConfig.backoff) {\n const strategy = mergedConfig.backoff()\n const nextRetryAt = strategy.getNextRetryAt(job.attempts + 1)\n\n debug('worker %s: job %s will retry at %s', this.#id, job.id, nextRetryAt.toISOString())\n\n await this.#rollbackJobWithBackoff(job, queue, nextRetryAt)\n\n return\n }\n\n await this.#rollbackJob(job, queue)\n }\n }\n\n async #acquireJobs(queue: string, count: number): Promise<AcquiredJob[]> {\n const jobs: AcquiredJob[] = []\n\n // Try to acquire up to `count` jobs\n for (let i = 0; i < count; i++) {\n const job = await this.#adapter.popFrom(queue)\n\n if (!job) {\n break\n }\n\n debug('worker %s: attempting to acquire lease for job %s', this.#id, job.id)\n\n try {\n const acquired = await this.#leaseManager.acquire(job.id)\n\n if (!acquired) {\n debug('worker %s: failed to acquire lease for job %s', this.#id, job.id)\n\n await this.#adapter.pushOn(queue, job)\n continue\n }\n\n debug('worker %s: acquired lease for job %s', this.#id, job.id)\n\n jobs.push({\n ...job,\n _lease: {\n commit: () => this.#commitJob(job.id),\n rollback: () => this.#rollbackJob(job, queue),\n },\n })\n } catch (error) {\n console.log(error)\n throw error\n }\n }\n\n return jobs\n }\n\n #commitJob(jobId: string) {\n debug('worker %s: committing job %s', this.#id, jobId)\n return this.#leaseManager.release(jobId)\n }\n\n async #rollbackJob(job: JobData, queue: string) {\n debug('worker %s: rolling back job %s', this.#id, job.id)\n\n const updatedJob = {\n ...job,\n attempts: (job.attempts || 0) + 1,\n }\n\n await Promise.all([this.#leaseManager.release(job.id), this.#adapter.pushOn(queue, updatedJob)])\n }\n\n async #rollbackJobWithBackoff(job: JobData, queue: string, nextRetryAt: Date) {\n debug('worker %s: rolling back job %s with backoff', this.#id, job.id)\n\n const updatedJob = {\n ...job,\n attempts: (job.attempts || 0) + 1,\n nextRetryAt,\n }\n\n await this.#leaseManager.release(job.id)\n\n const delay = nextRetryAt.getTime() - Date.now()\n\n if (delay > 0) {\n await this.#adapter.pushLaterOn(queue, updatedJob, delay)\n } else {\n await this.#adapter.pushOn(queue, updatedJob)\n }\n }\n\n async #setupGracefulShutdown() {\n const shutdown = async () => {\n debug('received shutdown signal, stopping worker...')\n await this.stop()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n }\n}\n","import * as errors from '#src/exceptions'\nimport debug from '#src/debug'\nimport { Locator } from '#src/locator'\nimport type { Adapter } from '#contracts/adapter'\nimport type { AdapterFactory, QueueConfig, QueueManagerConfig, RetryConfig } from '#types/main'\n\nclass QueueManagerSingleton {\n #defaultAdapter!: string\n #adapters: Record<string, AdapterFactory> = {}\n #globalRetryConfig?: RetryConfig\n #queueConfigs: Map<string, QueueConfig> = new Map()\n\n async init(config: QueueManagerConfig) {\n debug('initializing queue manager with config: %O', config)\n\n this.#validateConfig(config)\n\n this.#defaultAdapter = config.default\n this.#adapters = config.adapters\n this.#globalRetryConfig = config.retry\n\n if (config.queues) {\n for (const [queue, queueConfig] of Object.entries(config.queues)) {\n this.#queueConfigs.set(queue, queueConfig as QueueConfig)\n }\n }\n\n await Locator.registerFromGlob(config.locations)\n\n return this\n }\n\n use(adapter?: string): Adapter {\n if (!adapter) {\n adapter = this.#defaultAdapter\n }\n\n const adapterInstance = this.#adapters[adapter]\n\n if (!adapterInstance) {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${adapter}\" is not registered`])\n }\n\n debug('using adapter \"%s\"', adapter)\n\n try {\n return adapterInstance()\n } catch (error) {\n // TODO: Improve error handling\n throw new Error()\n // throw new errors.E_ADAPTER_ERROR(`Failed to initialize adapter \"${adapter}\"`, error as Error)\n }\n }\n\n /**\n * Priority: job > queue > global\n */\n getMergedRetryConfig(queue: string, jobRetryConfig?: RetryConfig): RetryConfig {\n const queueConfig = this.#queueConfigs.get(queue)\n const queueRetryConfig = queueConfig?.retry || {}\n\n let maxRetries =\n jobRetryConfig?.maxRetries ||\n queueRetryConfig.maxRetries ||\n this.#globalRetryConfig?.maxRetries ||\n 0\n\n let backoff =\n jobRetryConfig?.backoff || queueRetryConfig.backoff || this.#globalRetryConfig?.backoff\n\n return { maxRetries, backoff }\n }\n\n #validateConfig(config: QueueManagerConfig): void {\n if (!config.adapters || Object.keys(config.adapters).length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['At least one adapter must be configured'])\n }\n\n if (!config.default) {\n throw new errors.E_CONFIGURATION_ERROR(['Default adapter must be specified'])\n }\n\n if (!config.locations || config.locations.length === 0) {\n throw new errors.E_CONFIGURATION_ERROR(['Job locations must be specified'])\n }\n\n if (!config.adapters[config.default]) {\n throw new errors.E_CONFIGURATION_ERROR([\n `Default adapter \"${config.default}\" not found in adapters configuration`,\n ])\n }\n\n for (const [name, factory] of Object.entries(config.adapters)) {\n if (typeof factory !== 'function') {\n throw new errors.E_CONFIGURATION_ERROR([`Adapter \"${name}\" must be a factory function`])\n }\n }\n }\n\n async destroy() {\n for (const adapterName in this.#adapters) {\n const adapter = this.#adapters[adapterName]()\n await adapter.destroy()\n }\n }\n}\n\nexport const QueueManager = new QueueManagerSingleton()\n","import type { BackoffConfig, Duration } from '#types/main'\nimport * as errors from '#src/exceptions'\nimport { parse } from '#src/utils'\nimport { RuntimeException } from '@poppinss/utils'\nimport { assertUnreachable } from '@poppinss/utils/assert'\n\nexport class BackoffStrategy {\n readonly #config: BackoffConfig\n\n constructor(config: BackoffConfig) {\n this.#config = config\n this.#validateConfig()\n }\n\n calculateDelay(attempt: number): number {\n if (attempt < 1) {\n throw new RuntimeException('Attempt number must be >= 1')\n }\n\n const baseDelayMs = parse(this.#config.baseDelay)\n const maxDelayMs = this.#config.maxDelay ? parse(this.#config.maxDelay) : Infinity\n const multiplier = this.#config.multiplier ?? 2\n\n let delay: number\n\n switch (this.#config.strategy) {\n case 'exponential':\n delay = baseDelayMs * Math.pow(multiplier, attempt - 1)\n break\n case 'linear':\n delay = baseDelayMs * attempt\n break\n case 'fixed':\n delay = baseDelayMs\n break\n default:\n assertUnreachable(this.#config.strategy)\n }\n\n // Apply max delay limit\n delay = Math.min(delay, maxDelayMs)\n\n if (this.#config.jitter) {\n delay = this.#applyJitter(delay)\n }\n\n return Math.floor(delay)\n }\n\n getNextRetryAt(attempt: number): Date {\n const delay = this.calculateDelay(attempt)\n return new Date(Date.now() + delay)\n }\n\n getConfig(): Readonly<BackoffConfig> {\n return Object.freeze({ ...this.#config })\n }\n\n #validateConfig() {\n const baseDelayMs = parse(this.#config.baseDelay)\n\n if (baseDelayMs <= 0) {\n throw new errors.E_INVALID_BASE_DELAY([\n 'Base delay must be a positive integer greater than zero',\n ])\n }\n\n if (this.#config.maxDelay) {\n const maxDelayMs = parse(this.#config.maxDelay)\n\n if (maxDelayMs <= 0) {\n throw new errors.E_INVALID_MAX_DELAY([\n 'Max delay must be a positive integer greater than zero',\n ])\n }\n\n if (maxDelayMs <= baseDelayMs) {\n throw new errors.E_INVALID_MAX_DELAY(['Max delay should be greater than base delay'])\n }\n }\n\n if (this.#config.multiplier !== undefined) {\n if (this.#config.multiplier <= 0) {\n throw new errors.E_INVALID_MULTIPLIER([\n 'Multiplier must be a positive number greater than zero',\n ])\n }\n\n if (this.#config.strategy === 'exponential' && this.#config.multiplier < 1) {\n throw new errors.E_INVALID_MULTIPLIER(['Exponential strategy multiplier should be >= 1'])\n }\n }\n }\n\n #applyJitter(delay: number): number {\n const jitterRange = delay * 0.25\n const jitter = (Math.random() - 0.5) * 2 * jitterRange\n\n return Math.max(0, delay + jitter)\n }\n}\n\nexport function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'exponential',\n baseDelay: '1s',\n maxDelay: '5m',\n multiplier: 2,\n jitter: true,\n ...config,\n })\n}\n\nexport function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>) {\n return () =>\n new BackoffStrategy({\n strategy: 'linear',\n baseDelay: '5s',\n maxDelay: '2m',\n ...config,\n })\n}\n\nexport function fixedBackoff(delay: Duration = '10s') {\n return () =>\n new BackoffStrategy({\n strategy: 'fixed',\n baseDelay: delay,\n })\n}\n\nexport function customBackoff(config: BackoffConfig) {\n return () => new BackoffStrategy(config)\n}\n"],"mappings":"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","names":["errors","debug","Locator","QueueManager","errors","parse"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { LeaseManager } from './src/contracts/lease_manager.js';
|
|
2
|
+
|
|
3
|
+
declare class BackoffStrategy$1 {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(config: BackoffConfig);
|
|
6
|
+
calculateDelay(attempt: number): number;
|
|
7
|
+
getNextRetryAt(attempt: number): Date;
|
|
8
|
+
getConfig(): Readonly<BackoffConfig>;
|
|
9
|
+
}
|
|
10
|
+
declare function exponentialBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
|
|
11
|
+
declare function linearBackoff(config?: Partial<Omit<BackoffConfig, 'strategy'>>): () => BackoffStrategy$1;
|
|
12
|
+
declare function fixedBackoff(delay?: Duration): () => BackoffStrategy$1;
|
|
13
|
+
declare function customBackoff(config: BackoffConfig): () => BackoffStrategy$1;
|
|
14
|
+
|
|
15
|
+
type Duration = number | string;
|
|
16
|
+
interface AcquiredJob extends JobData {
|
|
17
|
+
_lease: {
|
|
18
|
+
commit: () => Promise<void>;
|
|
19
|
+
rollback: () => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
interface JobData {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
payload: any;
|
|
26
|
+
attempts: number;
|
|
27
|
+
priority?: number;
|
|
28
|
+
nextRetryAt?: Date;
|
|
29
|
+
}
|
|
30
|
+
interface JobOptions {
|
|
31
|
+
queue?: string;
|
|
32
|
+
adapter?: string | (() => Adapter);
|
|
33
|
+
maxRetries?: number;
|
|
34
|
+
priority?: number;
|
|
35
|
+
retry?: RetryConfig;
|
|
36
|
+
}
|
|
37
|
+
type JobClass<T extends Job = Job> = new (payload: any) => T;
|
|
38
|
+
interface RetryConfig {
|
|
39
|
+
maxRetries?: number;
|
|
40
|
+
backoff?: () => BackoffStrategy$1;
|
|
41
|
+
}
|
|
42
|
+
type BackoffStrategy = 'exponential' | 'linear' | 'fixed';
|
|
43
|
+
interface BackoffConfig {
|
|
44
|
+
strategy: BackoffStrategy;
|
|
45
|
+
baseDelay: Duration;
|
|
46
|
+
maxDelay?: Duration;
|
|
47
|
+
multiplier?: number;
|
|
48
|
+
jitter?: boolean;
|
|
49
|
+
}
|
|
50
|
+
interface LeaseConfig {
|
|
51
|
+
workerId: string;
|
|
52
|
+
leaseTimeout: Duration;
|
|
53
|
+
renewalInterval: Duration;
|
|
54
|
+
}
|
|
55
|
+
interface QueueConfig {
|
|
56
|
+
adapter?: string;
|
|
57
|
+
retry?: any;
|
|
58
|
+
}
|
|
59
|
+
interface WorkerConfig {
|
|
60
|
+
concurrency?: number;
|
|
61
|
+
pollingInterval?: Duration;
|
|
62
|
+
leaseTimeout?: Duration;
|
|
63
|
+
renewalInterval?: Duration;
|
|
64
|
+
}
|
|
65
|
+
type WorkerCycle = {
|
|
66
|
+
type: 'started';
|
|
67
|
+
queue: string;
|
|
68
|
+
job: any;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'completed';
|
|
71
|
+
queue: string;
|
|
72
|
+
job: any;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'idle';
|
|
75
|
+
suggestedDelay: Duration;
|
|
76
|
+
} | {
|
|
77
|
+
type: 'error';
|
|
78
|
+
error: Error;
|
|
79
|
+
suggestedDelay: Duration;
|
|
80
|
+
};
|
|
81
|
+
type AdapterFactory<T extends Adapter = Adapter> = () => T;
|
|
82
|
+
interface QueueManagerConfig {
|
|
83
|
+
default: string;
|
|
84
|
+
adapters: Record<string, AdapterFactory>;
|
|
85
|
+
retry?: RetryConfig;
|
|
86
|
+
queues?: Record<string, QueueConfig>;
|
|
87
|
+
worker?: WorkerConfig;
|
|
88
|
+
locations: string[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface Adapter {
|
|
92
|
+
createLeaseManager(config: LeaseConfig): LeaseManager;
|
|
93
|
+
size(): Promise<number>;
|
|
94
|
+
sizeOf(queue: string): Promise<number>;
|
|
95
|
+
push(jobData: JobData): Promise<void>;
|
|
96
|
+
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
97
|
+
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
98
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
99
|
+
pop(): Promise<JobData | null>;
|
|
100
|
+
popFrom(queue: string): Promise<JobData | null>;
|
|
101
|
+
destroy(): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
declare class JobDispatcher<T> {
|
|
105
|
+
#private;
|
|
106
|
+
constructor(name: string, payload: T);
|
|
107
|
+
toQueue(queue: string): this;
|
|
108
|
+
in(delay: Duration): this;
|
|
109
|
+
priority(priority: number): this;
|
|
110
|
+
with(adapter: string | (() => Adapter)): this;
|
|
111
|
+
run(): Promise<`${string}-${string}-${string}-${string}-${string}`>;
|
|
112
|
+
then(onFulfilled?: (value: string) => any, onRejected?: (reason: any) => any): Promise<any>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare abstract class Job<Payload = any> {
|
|
116
|
+
#private;
|
|
117
|
+
static options: JobOptions;
|
|
118
|
+
get payload(): Payload;
|
|
119
|
+
constructor(payload: Payload);
|
|
120
|
+
static dispatch<T extends Job>(this: new (payload: any) => T, payload: T extends Job<infer P> ? P : never): JobDispatcher<T extends Job<infer P> ? P : never>;
|
|
121
|
+
abstract execute(): Promise<void>;
|
|
122
|
+
failed?(error: Error): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { type Adapter as A, type BackoffStrategy as B, type Duration as D, Job as J, type LeaseConfig as L, type QueueManagerConfig as Q, type RetryConfig as R, type WorkerCycle as W, type JobData as a, type AcquiredJob as b, customBackoff as c, type JobOptions as d, exponentialBackoff as e, fixedBackoff as f, type JobClass as g, type BackoffConfig as h, type QueueConfig as i, type WorkerConfig as j, type AdapterFactory as k, linearBackoff as l };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=lease_manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Redis, RedisOptions } from 'ioredis';
|
|
2
|
+
import { A as Adapter, L as LeaseConfig, a as JobData } from '../../job-CcAUWe8j.js';
|
|
3
|
+
import { LeaseManager } from '../contracts/lease_manager.js';
|
|
4
|
+
|
|
5
|
+
type RedisConfig = Redis | RedisOptions;
|
|
6
|
+
declare function redis(config?: RedisConfig): () => RedisAdapter;
|
|
7
|
+
declare class RedisAdapter implements Adapter {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(connection: Redis);
|
|
10
|
+
createLeaseManager(config: LeaseConfig): LeaseManager;
|
|
11
|
+
destroy(): Promise<void>;
|
|
12
|
+
pop(): Promise<JobData | null>;
|
|
13
|
+
popFrom(queue: string): Promise<JobData | null>;
|
|
14
|
+
push(jobData: JobData): Promise<void>;
|
|
15
|
+
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
16
|
+
pushLaterOn(queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
17
|
+
pushOn(queue: string, jobData: JobData): Promise<void>;
|
|
18
|
+
size(): Promise<number>;
|
|
19
|
+
sizeOf(queue: string): Promise<number>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { RedisAdapter, redis };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/drivers/redis_adapter.ts
|
|
2
|
+
import { VerrouLeaseManager } from "#lease_managers/verrou";
|
|
3
|
+
import { Redis } from "ioredis";
|
|
4
|
+
var redisKey = "jobs";
|
|
5
|
+
var PROCESS_DELAYED_JOBS_SCRIPT = `
|
|
6
|
+
local delayed_key = KEYS[1]
|
|
7
|
+
local queue_key = KEYS[2]
|
|
8
|
+
local now = ARGV[1]
|
|
9
|
+
|
|
10
|
+
-- Get ready jobs (score <= now)
|
|
11
|
+
local ready_jobs = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)
|
|
12
|
+
|
|
13
|
+
if #ready_jobs > 0 then
|
|
14
|
+
-- Move jobs to priority queue and remove from delayed queue atomically
|
|
15
|
+
for i = 1, #ready_jobs do
|
|
16
|
+
local job_data = ready_jobs[i]
|
|
17
|
+
local job = cjson.decode(job_data)
|
|
18
|
+
local priority = job.priority or 5
|
|
19
|
+
redis.call('ZADD', queue_key, priority, job_data)
|
|
20
|
+
redis.call('ZREM', delayed_key, job_data)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
return #ready_jobs
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return 0
|
|
27
|
+
`;
|
|
28
|
+
function redis(config) {
|
|
29
|
+
return () => {
|
|
30
|
+
if (config instanceof Redis) {
|
|
31
|
+
return new RedisAdapter(config);
|
|
32
|
+
}
|
|
33
|
+
const options = {
|
|
34
|
+
host: "localhost",
|
|
35
|
+
port: 6379,
|
|
36
|
+
keyPrefix: "boringnode::queue::",
|
|
37
|
+
db: 0,
|
|
38
|
+
...config
|
|
39
|
+
};
|
|
40
|
+
const connection = new Redis(options);
|
|
41
|
+
return new RedisAdapter(connection);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
var RedisAdapter = class {
|
|
45
|
+
#connection;
|
|
46
|
+
constructor(connection) {
|
|
47
|
+
this.#connection = connection;
|
|
48
|
+
}
|
|
49
|
+
createLeaseManager(config) {
|
|
50
|
+
return new VerrouLeaseManager(config, this.#connection);
|
|
51
|
+
}
|
|
52
|
+
async destroy() {
|
|
53
|
+
await this.#connection.quit();
|
|
54
|
+
}
|
|
55
|
+
pop() {
|
|
56
|
+
return this.popFrom("default");
|
|
57
|
+
}
|
|
58
|
+
async popFrom(queue) {
|
|
59
|
+
await this.#processDelayedJobs(queue);
|
|
60
|
+
const queueContent = await this.#connection.zpopmin(`${redisKey}::${queue}`);
|
|
61
|
+
if (queueContent && queueContent.length > 0) {
|
|
62
|
+
return JSON.parse(queueContent[0]);
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
push(jobData) {
|
|
67
|
+
return this.pushOn("default", jobData);
|
|
68
|
+
}
|
|
69
|
+
pushLater(jobData, delay) {
|
|
70
|
+
return this.pushLaterOn("default", jobData, delay);
|
|
71
|
+
}
|
|
72
|
+
async pushLaterOn(queue, jobData, delay) {
|
|
73
|
+
const executeAt = Date.now() + delay;
|
|
74
|
+
const delayedKey = `${redisKey}::delayed::${queue}`;
|
|
75
|
+
await this.#connection.zadd(delayedKey, executeAt, JSON.stringify(jobData));
|
|
76
|
+
}
|
|
77
|
+
async pushOn(queue, jobData) {
|
|
78
|
+
const priority = jobData.priority ?? 5;
|
|
79
|
+
const timestamp = Date.now();
|
|
80
|
+
const score = priority * 1e13 + timestamp;
|
|
81
|
+
await this.#connection.zadd(`${redisKey}::${queue}`, score, JSON.stringify(jobData));
|
|
82
|
+
}
|
|
83
|
+
size() {
|
|
84
|
+
return this.sizeOf("default");
|
|
85
|
+
}
|
|
86
|
+
sizeOf(queue) {
|
|
87
|
+
return this.#connection.zcard(`${redisKey}::${queue}`);
|
|
88
|
+
}
|
|
89
|
+
async #processDelayedJobs(queue) {
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
const delayedKey = `${redisKey}::delayed::${queue}`;
|
|
92
|
+
const queueKey = `${redisKey}::${queue}`;
|
|
93
|
+
return await this.#connection.eval(
|
|
94
|
+
PROCESS_DELAYED_JOBS_SCRIPT,
|
|
95
|
+
2,
|
|
96
|
+
// number of keys
|
|
97
|
+
delayedKey,
|
|
98
|
+
queueKey,
|
|
99
|
+
now.toString()
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
export {
|
|
104
|
+
RedisAdapter,
|
|
105
|
+
redis
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=redis_adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/drivers/redis_adapter.ts"],"sourcesContent":["import { VerrouLeaseManager } from '#lease_managers/verrou'\nimport { Redis, type RedisOptions } from 'ioredis'\nimport type { Adapter } from '#contracts/adapter'\nimport type { JobData, LeaseConfig } from '#types/main'\nimport type { LeaseManager } from '#contracts/lease_manager'\n\nconst redisKey = 'jobs'\ntype RedisConfig = Redis | RedisOptions\n\n// Lua script for atomic delayed job processing\nconst PROCESS_DELAYED_JOBS_SCRIPT = `\n local delayed_key = KEYS[1]\n local queue_key = KEYS[2]\n local now = ARGV[1]\n\n -- Get ready jobs (score <= now)\n local ready_jobs = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)\n\n if #ready_jobs > 0 then\n -- Move jobs to priority queue and remove from delayed queue atomically\n for i = 1, #ready_jobs do\n local job_data = ready_jobs[i]\n local job = cjson.decode(job_data)\n local priority = job.priority or 5\n redis.call('ZADD', queue_key, priority, job_data)\n redis.call('ZREM', delayed_key, job_data)\n end\n\n return #ready_jobs\n end\n\n return 0\n`\n\nexport function redis(config?: RedisConfig) {\n return () => {\n if (config instanceof Redis) {\n return new RedisAdapter(config)\n }\n\n // Create new Redis instance from options\n const options: RedisOptions = {\n host: 'localhost',\n port: 6379,\n keyPrefix: 'boringnode::queue::',\n db: 0,\n ...config,\n }\n\n const connection = new Redis(options)\n return new RedisAdapter(connection)\n }\n}\n\nexport class RedisAdapter implements Adapter {\n readonly #connection: Redis\n\n constructor(connection: Redis) {\n this.#connection = connection\n }\n\n createLeaseManager(config: LeaseConfig): LeaseManager {\n return new VerrouLeaseManager(config, this.#connection)\n }\n\n async destroy(): Promise<void> {\n await this.#connection.quit()\n }\n\n pop(): Promise<JobData | null> {\n return this.popFrom('default')\n }\n\n async popFrom(queue: string): Promise<JobData | null> {\n // First, move any ready delayed jobs to the regular queue\n await this.#processDelayedJobs(queue)\n\n // Pop from priority queue (sorted set) - highest priority (lowest score) first\n const queueContent = await this.#connection.zpopmin(`${redisKey}::${queue}`)\n\n if (queueContent && queueContent.length > 0) {\n return JSON.parse(queueContent[0])\n }\n\n return null\n }\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n 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 const delayedKey = `${redisKey}::delayed::${queue}`\n\n await this.#connection.zadd(delayedKey, executeAt, JSON.stringify(jobData))\n }\n\n async pushOn(queue: string, jobData: JobData): Promise<void> {\n const priority = jobData.priority ?? 5\n\n // Use priority as primary score, add timestamp for FIFO order within same priority\n // Date.now() precision is sufficient but perfect FIFO within the same millisecond is not guaranteed\n const timestamp = Date.now()\n const score = priority * 1e13 + timestamp\n\n await this.#connection.zadd(`${redisKey}::${queue}`, score, JSON.stringify(jobData))\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(queue: string): Promise<number> {\n return this.#connection.zcard(`${redisKey}::${queue}`)\n }\n\n async #processDelayedJobs(queue: string): Promise<number> {\n const now = Date.now()\n const delayedKey = `${redisKey}::delayed::${queue}`\n const queueKey = `${redisKey}::${queue}`\n\n // Use Lua script for atomic operation - much faster than pipeline\n return (await this.#connection.eval(\n PROCESS_DELAYED_JOBS_SCRIPT,\n 2, // number of keys\n delayedKey,\n queueKey,\n now.toString()\n )) as number\n }\n}\n"],"mappings":";AAAA,SAAS,0BAA0B;AACnC,SAAS,aAAgC;AAKzC,IAAM,WAAW;AAIjB,IAAM,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwB7B,SAAS,MAAM,QAAsB;AAC1C,SAAO,MAAM;AACX,QAAI,kBAAkB,OAAO;AAC3B,aAAO,IAAI,aAAa,MAAM;AAAA,IAChC;AAGA,UAAM,UAAwB;AAAA,MAC5B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,MACX,IAAI;AAAA,MACJ,GAAG;AAAA,IACL;AAEA,UAAM,aAAa,IAAI,MAAM,OAAO;AACpC,WAAO,IAAI,aAAa,UAAU;AAAA,EACpC;AACF;AAEO,IAAM,eAAN,MAAsC;AAAA,EAClC;AAAA,EAET,YAAY,YAAmB;AAC7B,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,mBAAmB,QAAmC;AACpD,WAAO,IAAI,mBAAmB,QAAQ,KAAK,WAAW;AAAA,EACxD;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,KAAK,YAAY,KAAK;AAAA,EAC9B;AAAA,EAEA,MAA+B;AAC7B,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,MAAM,QAAQ,OAAwC;AAEpD,UAAM,KAAK,oBAAoB,KAAK;AAGpC,UAAM,eAAe,MAAM,KAAK,YAAY,QAAQ,GAAG,QAAQ,KAAK,KAAK,EAAE;AAE3E,QAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,aAAO,KAAK,MAAM,aAAa,CAAC,CAAC;AAAA,IACnC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,SAAiC;AACpC,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,UAAU,SAAkB,OAA8B;AACxD,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,MAAM,YAAY,OAAe,SAAkB,OAA8B;AAC/E,UAAM,YAAY,KAAK,IAAI,IAAI;AAC/B,UAAM,aAAa,GAAG,QAAQ,cAAc,KAAK;AAEjD,UAAM,KAAK,YAAY,KAAK,YAAY,WAAW,KAAK,UAAU,OAAO,CAAC;AAAA,EAC5E;AAAA,EAEA,MAAM,OAAO,OAAe,SAAiC;AAC3D,UAAM,WAAW,QAAQ,YAAY;AAIrC,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,QAAQ,WAAW,OAAO;AAEhC,UAAM,KAAK,YAAY,KAAK,GAAG,QAAQ,KAAK,KAAK,IAAI,OAAO,KAAK,UAAU,OAAO,CAAC;AAAA,EACrF;AAAA,EAEA,OAAwB;AACtB,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,OAAO,OAAgC;AACrC,WAAO,KAAK,YAAY,MAAM,GAAG,QAAQ,KAAK,KAAK,EAAE;AAAA,EACvD;AAAA,EAEA,MAAM,oBAAoB,OAAgC;AACxD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,aAAa,GAAG,QAAQ,cAAc,KAAK;AACjD,UAAM,WAAW,GAAG,QAAQ,KAAK,KAAK;AAGtC,WAAQ,MAAM,KAAK,YAAY;AAAA,MAC7B;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,SAAS;AAAA,IACf;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { A as Adapter, L as LeaseConfig, a as JobData } from '../../job-CcAUWe8j.js';
|
|
2
|
+
import { LeaseManager } from '../contracts/lease_manager.js';
|
|
3
|
+
|
|
4
|
+
declare function sync(): () => SyncAdapter;
|
|
5
|
+
declare class SyncAdapter implements Adapter {
|
|
6
|
+
#private;
|
|
7
|
+
createLeaseManager(_config: LeaseConfig): LeaseManager;
|
|
8
|
+
push(jobData: JobData): Promise<void>;
|
|
9
|
+
pushOn(_queue: string, jobData: JobData): Promise<void>;
|
|
10
|
+
pushLater(jobData: JobData, delay: number): Promise<void>;
|
|
11
|
+
pushLaterOn(_queue: string, jobData: JobData, delay: number): Promise<void>;
|
|
12
|
+
size(): Promise<number>;
|
|
13
|
+
sizeOf(_queue: string): Promise<number>;
|
|
14
|
+
pop(): Promise<JobData | null>;
|
|
15
|
+
popFrom(_queue: string): Promise<JobData | null>;
|
|
16
|
+
destroy(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { SyncAdapter, sync };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// src/drivers/sync_adapter.ts
|
|
2
|
+
import { Locator } from "#src/locator";
|
|
3
|
+
function sync() {
|
|
4
|
+
return () => new SyncAdapter();
|
|
5
|
+
}
|
|
6
|
+
var SyncAdapter = class {
|
|
7
|
+
createLeaseManager(_config) {
|
|
8
|
+
throw new Error("Method not implemented.");
|
|
9
|
+
}
|
|
10
|
+
push(jobData) {
|
|
11
|
+
return this.pushOn("default", jobData);
|
|
12
|
+
}
|
|
13
|
+
pushOn(_queue, jobData) {
|
|
14
|
+
return this.#execute(jobData.name, jobData.payload);
|
|
15
|
+
}
|
|
16
|
+
pushLater(jobData, delay) {
|
|
17
|
+
return this.pushLaterOn("default", jobData, delay);
|
|
18
|
+
}
|
|
19
|
+
pushLaterOn(_queue, jobData, delay) {
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
void this.#execute(jobData.name, jobData.payload);
|
|
22
|
+
}, delay);
|
|
23
|
+
return Promise.resolve();
|
|
24
|
+
}
|
|
25
|
+
size() {
|
|
26
|
+
return this.sizeOf("default");
|
|
27
|
+
}
|
|
28
|
+
sizeOf(_queue) {
|
|
29
|
+
return Promise.resolve(0);
|
|
30
|
+
}
|
|
31
|
+
pop() {
|
|
32
|
+
return this.popFrom("default");
|
|
33
|
+
}
|
|
34
|
+
popFrom(_queue) {
|
|
35
|
+
throw new Error("Method not implemented.");
|
|
36
|
+
}
|
|
37
|
+
destroy() {
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
}
|
|
40
|
+
async #execute(jobName, payload) {
|
|
41
|
+
const JobClass = Locator.get(jobName);
|
|
42
|
+
if (!JobClass) {
|
|
43
|
+
throw new Error(`Job class ${jobName} not found.`);
|
|
44
|
+
}
|
|
45
|
+
const jobInstance = new JobClass(payload);
|
|
46
|
+
await jobInstance.execute();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
SyncAdapter,
|
|
51
|
+
sync
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=sync_adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/drivers/sync_adapter.ts"],"sourcesContent":["import { Locator } from '#src/locator'\nimport type { Adapter } from '#contracts/adapter'\nimport type { JobData, LeaseConfig } from '#types/main'\nimport type { LeaseManager } from '#contracts/lease_manager'\n\nexport function sync() {\n return () => new SyncAdapter()\n}\n\nexport class SyncAdapter implements Adapter {\n createLeaseManager(_config: LeaseConfig): LeaseManager {\n throw new Error('Method not implemented.')\n }\n\n push(jobData: JobData): Promise<void> {\n return this.pushOn('default', jobData)\n }\n\n pushOn(_queue: string, jobData: JobData): Promise<void> {\n return this.#execute(jobData.name, jobData.payload)\n }\n\n pushLater(jobData: JobData, delay: number): Promise<void> {\n return this.pushLaterOn('default', jobData, delay)\n }\n\n pushLaterOn(_queue: string, jobData: JobData, delay: number): Promise<void> {\n setTimeout(() => {\n void this.#execute(jobData.name, jobData.payload)\n }, delay)\n\n return Promise.resolve()\n }\n\n size(): Promise<number> {\n return this.sizeOf('default')\n }\n\n sizeOf(_queue: string): Promise<number> {\n return Promise.resolve(0)\n }\n\n pop(): Promise<JobData | null> {\n return this.popFrom('default')\n }\n\n popFrom(_queue: string): Promise<JobData | null> {\n throw new Error('Method not implemented.')\n }\n\n destroy(): Promise<void> {\n return Promise.resolve()\n }\n\n async #execute(jobName: string, payload: any): Promise<any> {\n const JobClass = Locator.get(jobName)\n\n if (!JobClass) {\n throw new Error(`Job class ${jobName} not found.`)\n }\n\n const jobInstance = new JobClass(payload)\n await jobInstance.execute()\n }\n}\n"],"mappings":";AAAA,SAAS,eAAe;AAKjB,SAAS,OAAO;AACrB,SAAO,MAAM,IAAI,YAAY;AAC/B;AAEO,IAAM,cAAN,MAAqC;AAAA,EAC1C,mBAAmB,SAAoC;AACrD,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EAEA,KAAK,SAAiC;AACpC,WAAO,KAAK,OAAO,WAAW,OAAO;AAAA,EACvC;AAAA,EAEA,OAAO,QAAgB,SAAiC;AACtD,WAAO,KAAK,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAAA,EACpD;AAAA,EAEA,UAAU,SAAkB,OAA8B;AACxD,WAAO,KAAK,YAAY,WAAW,SAAS,KAAK;AAAA,EACnD;AAAA,EAEA,YAAY,QAAgB,SAAkB,OAA8B;AAC1E,eAAW,MAAM;AACf,WAAK,KAAK,SAAS,QAAQ,MAAM,QAAQ,OAAO;AAAA,IAClD,GAAG,KAAK;AAER,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,OAAwB;AACtB,WAAO,KAAK,OAAO,SAAS;AAAA,EAC9B;AAAA,EAEA,OAAO,QAAiC;AACtC,WAAO,QAAQ,QAAQ,CAAC;AAAA,EAC1B;AAAA,EAEA,MAA+B;AAC7B,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EAEA,QAAQ,QAAyC;AAC/C,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAAA,EAEA,UAAyB;AACvB,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAAA,EAEA,MAAM,SAAS,SAAiB,SAA4B;AAC1D,UAAM,WAAW,QAAQ,IAAI,OAAO;AAEpC,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,aAAa,OAAO,aAAa;AAAA,IACnD;AAEA,UAAM,cAAc,IAAI,SAAS,OAAO;AACxC,UAAM,YAAY,QAAQ;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { b as AcquiredJob, k as AdapterFactory, h as BackoffConfig, B as BackoffStrategy, D as Duration, g as JobClass, a as JobData, d as JobOptions, L as LeaseConfig, i as QueueConfig, Q as QueueManagerConfig, R as RetryConfig, j as WorkerConfig, W as WorkerCycle } from '../../job-CcAUWe8j.js';
|
|
2
|
+
import '../contracts/lease_manager.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boringnode/queue",
|
|
3
|
+
"description": "A simple and efficient queue system for Node.js applications",
|
|
4
|
+
"version": "0.0.1-alpha",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"build"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./build/index.js",
|
|
12
|
+
"./drivers/*": "./build/src/drivers/*.js",
|
|
13
|
+
"./contracts/*": "./build/src/contracts/*.js",
|
|
14
|
+
"./types/*": "./build/src/types/*.js"
|
|
15
|
+
},
|
|
16
|
+
"imports": {
|
|
17
|
+
"#src/*": "./src/*.ts",
|
|
18
|
+
"#drivers/*": "./src/drivers/*.ts",
|
|
19
|
+
"#lease_managers/*": "./src/lease_managers/*.ts",
|
|
20
|
+
"#contracts/*": "./src/contracts/*.ts",
|
|
21
|
+
"#strategies/*": "./src/strategies/*.ts",
|
|
22
|
+
"#types/*": "./src/types/*.ts"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "yarn clean && tsup-node",
|
|
26
|
+
"clean": "del-cli build",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"lint": "eslint .",
|
|
29
|
+
"prepublishOnly": "yarn build",
|
|
30
|
+
"release": "yarn dlx release-it",
|
|
31
|
+
"test": "c8 node --enable-source-maps bin/test.ts",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@lukeed/ms": "^2.0.2",
|
|
36
|
+
"@poppinss/utils": "^6.10.1",
|
|
37
|
+
"@verrou/core": "^0.5.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@adonisjs/eslint-config": "^2.1.2",
|
|
41
|
+
"@adonisjs/prettier-config": "^1.4.5",
|
|
42
|
+
"@adonisjs/tsconfig": "^1.4.1",
|
|
43
|
+
"@japa/assert": "^4.1.1",
|
|
44
|
+
"@japa/expect-type": "^2.0.3",
|
|
45
|
+
"@japa/file-system": "^2.3.2",
|
|
46
|
+
"@japa/runner": "^4.4.0",
|
|
47
|
+
"@types/node": "^24.3.1",
|
|
48
|
+
"c8": "^10.1.3",
|
|
49
|
+
"del-cli": "^7.0.0",
|
|
50
|
+
"eslint": "^9.35.0",
|
|
51
|
+
"ioredis": "^5.7.0",
|
|
52
|
+
"prettier": "^3.6.2",
|
|
53
|
+
"release-it": "^19.0.4",
|
|
54
|
+
"testcontainers": "^11.5.1",
|
|
55
|
+
"tsup": "^8.5.1",
|
|
56
|
+
"typescript": "^5.9.2"
|
|
57
|
+
},
|
|
58
|
+
"author": "Romain Lanz <romain.lanz@pm.me>",
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"keywords": [
|
|
61
|
+
"queue",
|
|
62
|
+
"job",
|
|
63
|
+
"task",
|
|
64
|
+
"worker",
|
|
65
|
+
"redis"
|
|
66
|
+
],
|
|
67
|
+
"prettier": "@adonisjs/prettier-config",
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public",
|
|
70
|
+
"tag": "latest"
|
|
71
|
+
},
|
|
72
|
+
"release-it": {
|
|
73
|
+
"git": {
|
|
74
|
+
"commitMessage": "chore(release): ${version}",
|
|
75
|
+
"tagAnnotation": "v${version}",
|
|
76
|
+
"tagName": "v${version}"
|
|
77
|
+
},
|
|
78
|
+
"github": {
|
|
79
|
+
"release": true,
|
|
80
|
+
"releaseName": "v${version}",
|
|
81
|
+
"web": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"packageManager": "yarn@4.9.4+sha512.7b1cb0b62abba6a537b3a2ce00811a843bea02bcf53138581a6ae5b1bf563f734872bd47de49ce32a9ca9dcaff995aa789577ffb16811da7c603dcf69e73750b",
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=24.0.0"
|
|
87
|
+
}
|
|
88
|
+
}
|