@c9up/bay 0.1.4 → 0.1.6
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/BayProvider.d.ts +7 -6
- package/dist/BayProvider.d.ts.map +1 -1
- package/dist/BayProvider.js.map +1 -1
- package/dist/QueueManager.d.ts +21 -2
- package/dist/QueueManager.d.ts.map +1 -1
- package/dist/QueueManager.js +34 -2
- package/dist/QueueManager.js.map +1 -1
- package/dist/drivers/RedisDriver.d.ts +9 -1
- package/dist/drivers/RedisDriver.d.ts.map +1 -1
- package/dist/drivers/RedisDriver.js +14 -1
- package/dist/drivers/RedisDriver.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/BayProvider.ts +7 -6
- package/src/QueueManager.ts +43 -2
- package/src/drivers/RedisDriver.ts +16 -1
- package/src/index.ts +11 -0
package/dist/BayProvider.d.ts
CHANGED
|
@@ -17,15 +17,16 @@ export interface BayAppContext {
|
|
|
17
17
|
}
|
|
18
18
|
export interface BayProviderConfig {
|
|
19
19
|
/**
|
|
20
|
-
* Driver to bind by default.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* `
|
|
20
|
+
* Driver to bind by default. The provider only auto-wires `"memory"`
|
|
21
|
+
* (the default) — it has no Redis connection to build a `RedisDriver`
|
|
22
|
+
* from. For Redis, a custom driver, or a pre-built instance, wire
|
|
23
|
+
* `QueueManager` directly in your app's startup and skip the provider;
|
|
24
|
+
* the `services/main` singleton resolves whatever is registered.
|
|
25
|
+
* Passing anything other than `"memory"` throws at boot.
|
|
25
26
|
*
|
|
26
27
|
* Default `"memory"`.
|
|
27
28
|
*/
|
|
28
|
-
driver?: "memory"
|
|
29
|
+
driver?: "memory";
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* BayProvider — registers a default in-memory `QueueManager` in the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BayProvider.d.ts","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,UAAU,YAAY;IACrB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AACD,UAAU,cAAc;IACvB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC
|
|
1
|
+
{"version":3,"file":"BayProvider.d.ts","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,UAAU,YAAY;IACrB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AACD,UAAU,cAAc;IACvB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AACD,MAAM,WAAW,aAAa;IAC7B,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IACjC;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IACnB,SAAS,CAAC,GAAG,EAAE,aAAa;gBAAlB,GAAG,EAAE,aAAa;IAExC,QAAQ,IAAI,IAAI;IAiBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B"}
|
package/dist/BayProvider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BayProvider.js","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"BayProvider.js","sourceRoot":"","sources":["../src/BayProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAkC9C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,OAAO,OAAO,WAAW;IACT;IAAtB,YAAsB,GAAkB;QAAlB,QAAG,GAAH,GAAG,CAAe;IAAG,CAAC;IAE5C,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAoB,OAAO,CAAC,CAAC;YAC/D,MAAM,UAAU,GAAG,MAAM,EAAE,MAAM,IAAI,QAAQ,CAAC;YAC9C,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACd,6BAA6B,UAAU,2BAA2B;oBACjE,sEAAsE,CACvE,CAAC;YACH,CAAC;YACD,OAAO,IAAI,YAAY,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,CAC1C,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CACtD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI;QACT,+DAA+D;QAC/D,+DAA+D;QAC/D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAe,YAAY,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,QAAQ,KAAmB,CAAC;CAClC"}
|
package/dist/QueueManager.d.ts
CHANGED
|
@@ -28,8 +28,15 @@ export interface QueueDriver {
|
|
|
28
28
|
retry(job: Job): Promise<void>;
|
|
29
29
|
failed(): Promise<Job[]>;
|
|
30
30
|
size(): Promise<number>;
|
|
31
|
+
/**
|
|
32
|
+
* Optional crash recovery: move jobs orphaned in the driver's 'processing'
|
|
33
|
+
* state (expired visibility lease) back to pending, returning the count
|
|
34
|
+
* recovered. In-memory drivers omit this — their jobs don't survive a crash.
|
|
35
|
+
*/
|
|
36
|
+
recoverStale?(): Promise<number>;
|
|
31
37
|
}
|
|
32
38
|
export declare class QueueManager {
|
|
39
|
+
#private;
|
|
33
40
|
private driver;
|
|
34
41
|
private handlers;
|
|
35
42
|
private running;
|
|
@@ -43,8 +50,20 @@ export declare class QueueManager {
|
|
|
43
50
|
}): Promise<string>;
|
|
44
51
|
/** Process the next job in the queue. */
|
|
45
52
|
processOne(): Promise<boolean>;
|
|
46
|
-
/**
|
|
47
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Start processing jobs continuously. Reclaims crash-orphaned jobs at
|
|
55
|
+
* startup and every `recoverStaleMs` thereafter (no-op for in-memory drivers
|
|
56
|
+
* without recoverStale) — otherwise a job left in 'processing' by a crashed
|
|
57
|
+
* worker would sit there forever.
|
|
58
|
+
*/
|
|
59
|
+
work(pollIntervalMs?: number, recoverStaleMs?: number): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Reclaim jobs orphaned by a crashed worker — moves entries stuck in the
|
|
62
|
+
* driver's 'processing' state (expired lease) back to pending and returns
|
|
63
|
+
* the count recovered. Returns 0 for in-memory drivers without recovery.
|
|
64
|
+
* Called automatically by work(); also safe to schedule manually.
|
|
65
|
+
*/
|
|
66
|
+
recoverStale(): Promise<number>;
|
|
48
67
|
/** Await the currently in-flight processOne, if any. */
|
|
49
68
|
drain(): Promise<void>;
|
|
50
69
|
/** Stop the worker. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"QueueManager.d.ts","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,YAAY;;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CACL;IACX,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAAiC;gBAE5C,MAAM,EAAE,WAAW;IAI/B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,UAAU,UAAU,CAAC,GAAG,IAAI;IAI1E,mCAAmC;IAC7B,QAAQ,CACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAChC,OAAO,CAAC,MAAM,CAAC;IAkBlB,yCAAyC;IACnC,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAwCpC;;;;;OAKG;IACG,IAAI,CAAC,cAAc,SAAO,EAAE,cAAc,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA8CzE;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAIrC,wDAAwD;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,uBAAuB;IACjB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,uBAAuB;IACjB,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAIlC,sBAAsB;IAChB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG7B"}
|
package/dist/QueueManager.js
CHANGED
|
@@ -72,15 +72,25 @@ export class QueueManager {
|
|
|
72
72
|
}
|
|
73
73
|
return true;
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
76
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Start processing jobs continuously. Reclaims crash-orphaned jobs at
|
|
77
|
+
* startup and every `recoverStaleMs` thereafter (no-op for in-memory drivers
|
|
78
|
+
* without recoverStale) — otherwise a job left in 'processing' by a crashed
|
|
79
|
+
* worker would sit there forever.
|
|
80
|
+
*/
|
|
81
|
+
async work(pollIntervalMs = 1000, recoverStaleMs = 30_000) {
|
|
77
82
|
if (pollIntervalMs <= 0) {
|
|
78
83
|
throw new Error("pollIntervalMs must be positive");
|
|
79
84
|
}
|
|
85
|
+
if (recoverStaleMs <= 0) {
|
|
86
|
+
throw new Error("recoverStaleMs must be positive");
|
|
87
|
+
}
|
|
80
88
|
if (this.running) {
|
|
81
89
|
throw new Error("QueueManager is already running");
|
|
82
90
|
}
|
|
83
91
|
this.running = true;
|
|
92
|
+
await this.#tryRecoverStale();
|
|
93
|
+
let lastRecover = Date.now();
|
|
84
94
|
while (this.running) {
|
|
85
95
|
try {
|
|
86
96
|
this.inflightPromise = this.processOne();
|
|
@@ -96,8 +106,30 @@ export class QueueManager {
|
|
|
96
106
|
finally {
|
|
97
107
|
this.inflightPromise = null;
|
|
98
108
|
}
|
|
109
|
+
if (this.running && Date.now() - lastRecover >= recoverStaleMs) {
|
|
110
|
+
await this.#tryRecoverStale();
|
|
111
|
+
lastRecover = Date.now();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** recoverStale() wrapper that swallows driver errors — used by the work loop. */
|
|
116
|
+
async #tryRecoverStale() {
|
|
117
|
+
try {
|
|
118
|
+
await this.recoverStale();
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
process.stderr.write(`QueueManager recoverStale error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
99
122
|
}
|
|
100
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Reclaim jobs orphaned by a crashed worker — moves entries stuck in the
|
|
126
|
+
* driver's 'processing' state (expired lease) back to pending and returns
|
|
127
|
+
* the count recovered. Returns 0 for in-memory drivers without recovery.
|
|
128
|
+
* Called automatically by work(); also safe to schedule manually.
|
|
129
|
+
*/
|
|
130
|
+
async recoverStale() {
|
|
131
|
+
return (await this.driver.recoverStale?.()) ?? 0;
|
|
132
|
+
}
|
|
101
133
|
/** Await the currently in-flight processOne, if any. */
|
|
102
134
|
async drain() {
|
|
103
135
|
if (this.inflightPromise) {
|
package/dist/QueueManager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueManager.js","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"QueueManager.js","sourceRoot":"","sources":["../src/QueueManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
* - If a worker crashes, the job stays in processing
|
|
8
8
|
* - recoverStale() moves expired processing jobs back to pending
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Without LMOVE (Redis <6.2) pop() falls back to a non-atomic lpop+rpush, which
|
|
11
|
+
* downgrades delivery to at-most-once — a crash between the two commands drops
|
|
12
|
+
* the in-flight job (recoverStale can't reclaim it, it was never in processing).
|
|
13
|
+
* The constructor warns when the client lacks LMOVE so the downgrade isn't silent.
|
|
14
|
+
*
|
|
15
|
+
* The client must be ioredis-shaped: lowercase methods and positional options
|
|
16
|
+
* (e.g. set(key, val, "PX", ms)). node-redis v4 (camelCase + options objects like
|
|
17
|
+
* { PX: ms }) does NOT satisfy this interface and would drop the lease TTL — it
|
|
18
|
+
* needs a thin adapter.
|
|
11
19
|
*/
|
|
12
20
|
import type { Job, QueueDriver } from "../QueueManager.js";
|
|
13
21
|
export interface RedisClient {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"RedisDriver.d.ts","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC3B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,CACL,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,OAAO,EACtB,EAAE,EAAE,MAAM,GAAG,OAAO,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACzC;AAcD,qBAAa,WAAY,YAAW,WAAW;;gBAM7C,MAAM,EAAE,WAAW,EACnB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE;IAmBtD,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7B,GAAG,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAsC1B,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA4D/B,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAcxB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CAG7B"}
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
* - If a worker crashes, the job stays in processing
|
|
8
8
|
* - recoverStale() moves expired processing jobs back to pending
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Without LMOVE (Redis <6.2) pop() falls back to a non-atomic lpop+rpush, which
|
|
11
|
+
* downgrades delivery to at-most-once — a crash between the two commands drops
|
|
12
|
+
* the in-flight job (recoverStale can't reclaim it, it was never in processing).
|
|
13
|
+
* The constructor warns when the client lacks LMOVE so the downgrade isn't silent.
|
|
14
|
+
*
|
|
15
|
+
* The client must be ioredis-shaped: lowercase methods and positional options
|
|
16
|
+
* (e.g. set(key, val, "PX", ms)). node-redis v4 (camelCase + options objects like
|
|
17
|
+
* { PX: ms }) does NOT satisfy this interface and would drop the lease TTL — it
|
|
18
|
+
* needs a thin adapter.
|
|
11
19
|
*/
|
|
12
20
|
function isValidJob(obj) {
|
|
13
21
|
if (typeof obj !== "object" || obj === null)
|
|
@@ -27,6 +35,11 @@ export class RedisDriver {
|
|
|
27
35
|
this.#client = client;
|
|
28
36
|
this.#prefix = options?.prefix ?? "queue:";
|
|
29
37
|
this.#visibilityTimeout = options?.visibilityTimeoutMs ?? 30_000;
|
|
38
|
+
if (typeof client.lmove !== "function") {
|
|
39
|
+
console.warn("[bay] RedisDriver: client lacks LMOVE (Redis <6.2). pop() falls back to " +
|
|
40
|
+
"a non-atomic lpop+rpush, downgrading delivery from at-least-once to " +
|
|
41
|
+
"at-most-once — a crash between the two commands loses the in-flight job.");
|
|
42
|
+
}
|
|
30
43
|
}
|
|
31
44
|
#pendingKey = () => `${this.#prefix}pending`;
|
|
32
45
|
#processingKey = () => `${this.#prefix}processing`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisDriver.js","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"RedisDriver.js","sourceRoot":"","sources":["../../src/drivers/RedisDriver.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @implements MISS-11
|
|
7
7
|
*/
|
|
8
|
+
export type { BayProviderConfig } from "./BayProvider.js";
|
|
8
9
|
export { MemoryDriver } from "./drivers/MemoryDriver.js";
|
|
9
10
|
export type { RedisClient } from "./drivers/RedisDriver.js";
|
|
10
11
|
export { RedisDriver } from "./drivers/RedisDriver.js";
|
|
11
12
|
export type { Job, JobHandler, QueueDriver } from "./QueueManager.js";
|
|
12
13
|
export { QueueManager } from "./QueueManager.js";
|
|
14
|
+
import type { BayProviderConfig } from "./BayProvider.js";
|
|
15
|
+
/**
|
|
16
|
+
* Author-time config helper for `config/queue.ts` — AdonisJS `defineConfig`
|
|
17
|
+
* parity. Identity at runtime; the generic preserves literal types for inference.
|
|
18
|
+
*/
|
|
19
|
+
export declare function defineConfig<T extends BayProviderConfig>(config: T): T;
|
|
13
20
|
//# 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":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAEtE"}
|
package/dist/index.js
CHANGED
|
@@ -8,4 +8,11 @@
|
|
|
8
8
|
export { MemoryDriver } from "./drivers/MemoryDriver.js";
|
|
9
9
|
export { RedisDriver } from "./drivers/RedisDriver.js";
|
|
10
10
|
export { QueueManager } from "./QueueManager.js";
|
|
11
|
+
/**
|
|
12
|
+
* Author-time config helper for `config/queue.ts` — AdonisJS `defineConfig`
|
|
13
|
+
* parity. Identity at runtime; the generic preserves literal types for inference.
|
|
14
|
+
*/
|
|
15
|
+
export function defineConfig(config) {
|
|
16
|
+
return config;
|
|
17
|
+
}
|
|
11
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAA8B,MAAS;IAClE,OAAO,MAAM,CAAC;AACf,CAAC"}
|
package/package.json
CHANGED
package/src/BayProvider.ts
CHANGED
|
@@ -22,15 +22,16 @@ export interface BayAppContext {
|
|
|
22
22
|
|
|
23
23
|
export interface BayProviderConfig {
|
|
24
24
|
/**
|
|
25
|
-
* Driver to bind by default.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* `
|
|
25
|
+
* Driver to bind by default. The provider only auto-wires `"memory"`
|
|
26
|
+
* (the default) — it has no Redis connection to build a `RedisDriver`
|
|
27
|
+
* from. For Redis, a custom driver, or a pre-built instance, wire
|
|
28
|
+
* `QueueManager` directly in your app's startup and skip the provider;
|
|
29
|
+
* the `services/main` singleton resolves whatever is registered.
|
|
30
|
+
* Passing anything other than `"memory"` throws at boot.
|
|
30
31
|
*
|
|
31
32
|
* Default `"memory"`.
|
|
32
33
|
*/
|
|
33
|
-
driver?: "memory"
|
|
34
|
+
driver?: "memory";
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
package/src/QueueManager.ts
CHANGED
|
@@ -31,6 +31,12 @@ export interface QueueDriver {
|
|
|
31
31
|
retry(job: Job): Promise<void>;
|
|
32
32
|
failed(): Promise<Job[]>;
|
|
33
33
|
size(): Promise<number>;
|
|
34
|
+
/**
|
|
35
|
+
* Optional crash recovery: move jobs orphaned in the driver's 'processing'
|
|
36
|
+
* state (expired visibility lease) back to pending, returning the count
|
|
37
|
+
* recovered. In-memory drivers omit this — their jobs don't survive a crash.
|
|
38
|
+
*/
|
|
39
|
+
recoverStale?(): Promise<number>;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
export class QueueManager {
|
|
@@ -113,15 +119,25 @@ export class QueueManager {
|
|
|
113
119
|
return true;
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
/**
|
|
117
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Start processing jobs continuously. Reclaims crash-orphaned jobs at
|
|
124
|
+
* startup and every `recoverStaleMs` thereafter (no-op for in-memory drivers
|
|
125
|
+
* without recoverStale) — otherwise a job left in 'processing' by a crashed
|
|
126
|
+
* worker would sit there forever.
|
|
127
|
+
*/
|
|
128
|
+
async work(pollIntervalMs = 1000, recoverStaleMs = 30_000): Promise<void> {
|
|
118
129
|
if (pollIntervalMs <= 0) {
|
|
119
130
|
throw new Error("pollIntervalMs must be positive");
|
|
120
131
|
}
|
|
132
|
+
if (recoverStaleMs <= 0) {
|
|
133
|
+
throw new Error("recoverStaleMs must be positive");
|
|
134
|
+
}
|
|
121
135
|
if (this.running) {
|
|
122
136
|
throw new Error("QueueManager is already running");
|
|
123
137
|
}
|
|
124
138
|
this.running = true;
|
|
139
|
+
await this.#tryRecoverStale();
|
|
140
|
+
let lastRecover = Date.now();
|
|
125
141
|
while (this.running) {
|
|
126
142
|
try {
|
|
127
143
|
this.inflightPromise = this.processOne();
|
|
@@ -137,9 +153,34 @@ export class QueueManager {
|
|
|
137
153
|
} finally {
|
|
138
154
|
this.inflightPromise = null;
|
|
139
155
|
}
|
|
156
|
+
if (this.running && Date.now() - lastRecover >= recoverStaleMs) {
|
|
157
|
+
await this.#tryRecoverStale();
|
|
158
|
+
lastRecover = Date.now();
|
|
159
|
+
}
|
|
140
160
|
}
|
|
141
161
|
}
|
|
142
162
|
|
|
163
|
+
/** recoverStale() wrapper that swallows driver errors — used by the work loop. */
|
|
164
|
+
async #tryRecoverStale(): Promise<void> {
|
|
165
|
+
try {
|
|
166
|
+
await this.recoverStale();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
process.stderr.write(
|
|
169
|
+
`QueueManager recoverStale error: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Reclaim jobs orphaned by a crashed worker — moves entries stuck in the
|
|
176
|
+
* driver's 'processing' state (expired lease) back to pending and returns
|
|
177
|
+
* the count recovered. Returns 0 for in-memory drivers without recovery.
|
|
178
|
+
* Called automatically by work(); also safe to schedule manually.
|
|
179
|
+
*/
|
|
180
|
+
async recoverStale(): Promise<number> {
|
|
181
|
+
return (await this.driver.recoverStale?.()) ?? 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
143
184
|
/** Await the currently in-flight processOne, if any. */
|
|
144
185
|
async drain(): Promise<void> {
|
|
145
186
|
if (this.inflightPromise) {
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
* - If a worker crashes, the job stays in processing
|
|
8
8
|
* - recoverStale() moves expired processing jobs back to pending
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Without LMOVE (Redis <6.2) pop() falls back to a non-atomic lpop+rpush, which
|
|
11
|
+
* downgrades delivery to at-most-once — a crash between the two commands drops
|
|
12
|
+
* the in-flight job (recoverStale can't reclaim it, it was never in processing).
|
|
13
|
+
* The constructor warns when the client lacks LMOVE so the downgrade isn't silent.
|
|
14
|
+
*
|
|
15
|
+
* The client must be ioredis-shaped: lowercase methods and positional options
|
|
16
|
+
* (e.g. set(key, val, "PX", ms)). node-redis v4 (camelCase + options objects like
|
|
17
|
+
* { PX: ms }) does NOT satisfy this interface and would drop the lease TTL — it
|
|
18
|
+
* needs a thin adapter.
|
|
11
19
|
*/
|
|
12
20
|
|
|
13
21
|
import type { Job, QueueDriver } from "../QueueManager.js";
|
|
@@ -53,6 +61,13 @@ export class RedisDriver implements QueueDriver {
|
|
|
53
61
|
this.#client = client;
|
|
54
62
|
this.#prefix = options?.prefix ?? "queue:";
|
|
55
63
|
this.#visibilityTimeout = options?.visibilityTimeoutMs ?? 30_000;
|
|
64
|
+
if (typeof client.lmove !== "function") {
|
|
65
|
+
console.warn(
|
|
66
|
+
"[bay] RedisDriver: client lacks LMOVE (Redis <6.2). pop() falls back to " +
|
|
67
|
+
"a non-atomic lpop+rpush, downgrading delivery from at-least-once to " +
|
|
68
|
+
"at-most-once — a crash between the two commands loses the in-flight job.",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
#pendingKey = () => `${this.#prefix}pending`;
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,19 @@
|
|
|
6
6
|
* @implements MISS-11
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export type { BayProviderConfig } from "./BayProvider.js";
|
|
9
10
|
export { MemoryDriver } from "./drivers/MemoryDriver.js";
|
|
10
11
|
export type { RedisClient } from "./drivers/RedisDriver.js";
|
|
11
12
|
export { RedisDriver } from "./drivers/RedisDriver.js";
|
|
12
13
|
export type { Job, JobHandler, QueueDriver } from "./QueueManager.js";
|
|
13
14
|
export { QueueManager } from "./QueueManager.js";
|
|
15
|
+
|
|
16
|
+
import type { BayProviderConfig } from "./BayProvider.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Author-time config helper for `config/queue.ts` — AdonisJS `defineConfig`
|
|
20
|
+
* parity. Identity at runtime; the generic preserves literal types for inference.
|
|
21
|
+
*/
|
|
22
|
+
export function defineConfig<T extends BayProviderConfig>(config: T): T {
|
|
23
|
+
return config;
|
|
24
|
+
}
|