@checkstack/queue-bullmq-backend 0.1.5 → 0.2.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/CHANGELOG.md +27 -0
- package/package.json +1 -1
- package/src/bullmq-queue.ts +29 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @checkstack/queue-bullmq-backend
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2c0822d: ### Queue System
|
|
8
|
+
|
|
9
|
+
- Added cron pattern support to `scheduleRecurring()` - accepts either `intervalSeconds` or `cronPattern`
|
|
10
|
+
- BullMQ backend uses native cron scheduling via `pattern` option
|
|
11
|
+
- InMemoryQueue implements wall-clock cron scheduling with `cron-parser`
|
|
12
|
+
|
|
13
|
+
### Maintenance Backend
|
|
14
|
+
|
|
15
|
+
- Auto status transitions now use cron pattern `* * * * *` for precise second-0 scheduling
|
|
16
|
+
- User notifications are now sent for auto-started and auto-completed maintenances
|
|
17
|
+
- Refactored to call `addUpdate` RPC for status changes, centralizing hook/signal/notification logic
|
|
18
|
+
|
|
19
|
+
### UI
|
|
20
|
+
|
|
21
|
+
- DateTimePicker now resets seconds and milliseconds to 0 when time is changed
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [2c0822d]
|
|
26
|
+
- Updated dependencies [66a3963]
|
|
27
|
+
- @checkstack/queue-api@0.2.0
|
|
28
|
+
- @checkstack/backend-api@0.5.0
|
|
29
|
+
|
|
3
30
|
## 0.1.5
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/package.json
CHANGED
package/src/bullmq-queue.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
QueueStats,
|
|
6
6
|
ConsumeOptions,
|
|
7
7
|
RecurringJobDetails,
|
|
8
|
+
RecurringSchedule,
|
|
8
9
|
} from "@checkstack/queue-api";
|
|
9
10
|
import { Queue as BullQueue, Worker, JobsOptions } from "bullmq";
|
|
10
11
|
import type { BullMQConfig } from "./plugin";
|
|
@@ -25,7 +26,10 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
25
26
|
private consumerGroups = new Map<string, ConsumerGroupState>();
|
|
26
27
|
private stopped = false;
|
|
27
28
|
|
|
28
|
-
constructor(
|
|
29
|
+
constructor(
|
|
30
|
+
private name: string,
|
|
31
|
+
private config: BullMQConfig,
|
|
32
|
+
) {
|
|
29
33
|
// Initialize BullMQ Queue with Redis connection
|
|
30
34
|
this.queue = new BullQueue(name, {
|
|
31
35
|
connection: {
|
|
@@ -55,7 +59,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
55
59
|
} catch (error) {
|
|
56
60
|
const message = error instanceof Error ? error.message : String(error);
|
|
57
61
|
throw new Error(
|
|
58
|
-
`Failed to connect to Redis at ${this.config.host}:${this.config.port}: ${message}
|
|
62
|
+
`Failed to connect to Redis at ${this.config.host}:${this.config.port}: ${message}`,
|
|
59
63
|
);
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -66,7 +70,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
66
70
|
priority?: number;
|
|
67
71
|
startDelay?: number;
|
|
68
72
|
jobId?: string;
|
|
69
|
-
}
|
|
73
|
+
},
|
|
70
74
|
): Promise<string> {
|
|
71
75
|
if (this.stopped) {
|
|
72
76
|
throw new Error("Queue has been stopped");
|
|
@@ -93,7 +97,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
93
97
|
|
|
94
98
|
async consume(
|
|
95
99
|
consumer: QueueConsumer<T>,
|
|
96
|
-
options: ConsumeOptions
|
|
100
|
+
options: ConsumeOptions,
|
|
97
101
|
): Promise<void> {
|
|
98
102
|
if (this.stopped) {
|
|
99
103
|
throw new Error("Queue has been stopped");
|
|
@@ -138,7 +142,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
138
142
|
return Math.pow(2, attemptsMade) * 1000;
|
|
139
143
|
},
|
|
140
144
|
},
|
|
141
|
-
}
|
|
145
|
+
},
|
|
142
146
|
);
|
|
143
147
|
|
|
144
148
|
// Configure retries at job level via job options
|
|
@@ -147,7 +151,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
147
151
|
// Max retries exhausted
|
|
148
152
|
console.debug(
|
|
149
153
|
`Job ${job.id} exhausted retries (${job.attemptsMade}/${maxRetries}):`,
|
|
150
|
-
err
|
|
154
|
+
err,
|
|
151
155
|
);
|
|
152
156
|
}
|
|
153
157
|
});
|
|
@@ -164,33 +168,29 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
164
168
|
data: T,
|
|
165
169
|
options: {
|
|
166
170
|
jobId: string;
|
|
167
|
-
intervalSeconds: number;
|
|
168
|
-
startDelay?: number;
|
|
169
171
|
priority?: number;
|
|
170
|
-
}
|
|
172
|
+
} & RecurringSchedule,
|
|
171
173
|
): Promise<string> {
|
|
172
174
|
if (this.stopped) {
|
|
173
175
|
throw new Error("Queue has been stopped");
|
|
174
176
|
}
|
|
175
177
|
|
|
176
|
-
const { jobId,
|
|
178
|
+
const { jobId, priority } = options;
|
|
177
179
|
|
|
178
180
|
// Use upsertJobScheduler for create-or-update semantics
|
|
181
|
+
// BullMQ supports either 'every' (interval) or 'pattern' (cron)
|
|
179
182
|
await this.queue.upsertJobScheduler(
|
|
180
183
|
jobId,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
? new Date(Date.now() + startDelay * 1000)
|
|
185
|
-
: undefined,
|
|
186
|
-
},
|
|
184
|
+
"cronPattern" in options && options.cronPattern
|
|
185
|
+
? { pattern: options.cronPattern }
|
|
186
|
+
: { every: options.intervalSeconds! * 1000 },
|
|
187
187
|
{
|
|
188
188
|
name: this.name,
|
|
189
189
|
data,
|
|
190
190
|
opts: {
|
|
191
191
|
priority,
|
|
192
192
|
},
|
|
193
|
-
}
|
|
193
|
+
},
|
|
194
194
|
);
|
|
195
195
|
|
|
196
196
|
return jobId;
|
|
@@ -214,7 +214,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
async getRecurringJobDetails(
|
|
217
|
-
jobId: string
|
|
217
|
+
jobId: string,
|
|
218
218
|
): Promise<RecurringJobDetails<T> | undefined> {
|
|
219
219
|
if (this.stopped) {
|
|
220
220
|
throw new Error("Queue has been stopped");
|
|
@@ -228,13 +228,21 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// BullMQ scheduler template contains the data
|
|
231
|
-
|
|
231
|
+
// Determine if it's cron-based or interval-based
|
|
232
|
+
const baseDetails = {
|
|
232
233
|
jobId,
|
|
233
234
|
data: scheduler.template?.data as T,
|
|
234
|
-
intervalSeconds: scheduler.every ? scheduler.every / 1000 : 0,
|
|
235
235
|
priority: scheduler.template?.opts?.priority,
|
|
236
236
|
nextRunAt: scheduler.next ? new Date(scheduler.next) : undefined,
|
|
237
237
|
};
|
|
238
|
+
|
|
239
|
+
if (scheduler.pattern) {
|
|
240
|
+
return { ...baseDetails, cronPattern: scheduler.pattern };
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
...baseDetails,
|
|
244
|
+
intervalSeconds: scheduler.every ? scheduler.every / 1000 : 0,
|
|
245
|
+
};
|
|
238
246
|
}
|
|
239
247
|
|
|
240
248
|
async getInFlightCount(): Promise<number> {
|
|
@@ -267,7 +275,7 @@ export class BullMQQueue<T = unknown> implements Queue<T> {
|
|
|
267
275
|
"waiting",
|
|
268
276
|
"active",
|
|
269
277
|
"completed",
|
|
270
|
-
"failed"
|
|
278
|
+
"failed",
|
|
271
279
|
);
|
|
272
280
|
|
|
273
281
|
return {
|