@monque/core 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +20 -0
- package/dist/index.cjs +266 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +21 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +266 -67
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/scheduler/monque.ts +25 -1
- package/src/scheduler/services/change-stream-handler.ts +222 -51
- package/src/scheduler/services/job-manager.ts +4 -1
- package/src/scheduler/services/job-processor.ts +43 -6
- package/src/scheduler/services/job-scheduler.ts +18 -4
- package/src/scheduler/services/lifecycle-manager.ts +72 -17
- package/src/scheduler/services/types.ts +4 -0
- package/src/scheduler/types.ts +14 -0
|
@@ -21,19 +21,26 @@ interface TimerCallbacks {
|
|
|
21
21
|
poll: () => Promise<void>;
|
|
22
22
|
/** Update heartbeats for claimed jobs */
|
|
23
23
|
updateHeartbeats: () => Promise<void>;
|
|
24
|
+
/** Whether change streams are currently active */
|
|
25
|
+
isChangeStreamActive: () => boolean;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Manages scheduler lifecycle timers and job cleanup.
|
|
28
30
|
*
|
|
29
|
-
* Owns poll
|
|
31
|
+
* Owns poll scheduling, heartbeat interval, cleanup interval, and the
|
|
30
32
|
* cleanupJobs logic. Extracted from Monque to keep the facade thin.
|
|
31
33
|
*
|
|
34
|
+
* Uses adaptive poll scheduling: when change streams are active, polls at
|
|
35
|
+
* `safetyPollInterval` (safety net only). When change streams are inactive,
|
|
36
|
+
* polls at `pollInterval` (primary discovery mechanism).
|
|
37
|
+
*
|
|
32
38
|
* @internal Not part of public API.
|
|
33
39
|
*/
|
|
34
40
|
export class LifecycleManager {
|
|
35
41
|
private readonly ctx: SchedulerContext;
|
|
36
|
-
private
|
|
42
|
+
private callbacks: TimerCallbacks | null = null;
|
|
43
|
+
private pollTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
37
44
|
private heartbeatIntervalId: ReturnType<typeof setInterval> | null = null;
|
|
38
45
|
private cleanupIntervalId: ReturnType<typeof setInterval> | null = null;
|
|
39
46
|
|
|
@@ -44,18 +51,13 @@ export class LifecycleManager {
|
|
|
44
51
|
/**
|
|
45
52
|
* Start all lifecycle timers.
|
|
46
53
|
*
|
|
47
|
-
* Sets up poll
|
|
54
|
+
* Sets up adaptive poll scheduling, heartbeat interval, and (if configured)
|
|
48
55
|
* cleanup interval. Runs an initial poll immediately.
|
|
49
56
|
*
|
|
50
57
|
* @param callbacks - Functions to invoke on each timer tick
|
|
51
58
|
*/
|
|
52
59
|
startTimers(callbacks: TimerCallbacks): void {
|
|
53
|
-
|
|
54
|
-
this.pollIntervalId = setInterval(() => {
|
|
55
|
-
callbacks.poll().catch((error: unknown) => {
|
|
56
|
-
this.ctx.emit('job:error', { error: toError(error) });
|
|
57
|
-
});
|
|
58
|
-
}, this.ctx.options.pollInterval);
|
|
60
|
+
this.callbacks = callbacks;
|
|
59
61
|
|
|
60
62
|
// Start heartbeat interval for claimed jobs
|
|
61
63
|
this.heartbeatIntervalId = setInterval(() => {
|
|
@@ -80,26 +82,26 @@ export class LifecycleManager {
|
|
|
80
82
|
}, interval);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
// Run initial poll immediately
|
|
84
|
-
|
|
85
|
-
this.ctx.emit('job:error', { error: toError(error) });
|
|
86
|
-
});
|
|
85
|
+
// Run initial poll immediately, then schedule the next one adaptively
|
|
86
|
+
this.executePollAndScheduleNext();
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Stop all lifecycle timers.
|
|
91
91
|
*
|
|
92
|
-
* Clears poll, heartbeat, and cleanup
|
|
92
|
+
* Clears poll timeout, heartbeat interval, and cleanup interval.
|
|
93
93
|
*/
|
|
94
94
|
stopTimers(): void {
|
|
95
|
+
this.callbacks = null;
|
|
96
|
+
|
|
95
97
|
if (this.cleanupIntervalId) {
|
|
96
98
|
clearInterval(this.cleanupIntervalId);
|
|
97
99
|
this.cleanupIntervalId = null;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
if (this.
|
|
101
|
-
|
|
102
|
-
this.
|
|
102
|
+
if (this.pollTimeoutId) {
|
|
103
|
+
clearTimeout(this.pollTimeoutId);
|
|
104
|
+
this.pollTimeoutId = null;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
if (this.heartbeatIntervalId) {
|
|
@@ -108,6 +110,59 @@ export class LifecycleManager {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Reset the poll timer to reschedule the next poll.
|
|
115
|
+
*
|
|
116
|
+
* Called after change-stream-triggered polls to ensure the safety poll timer
|
|
117
|
+
* is recalculated (not fired redundantly from an old schedule).
|
|
118
|
+
*/
|
|
119
|
+
resetPollTimer(): void {
|
|
120
|
+
this.scheduleNextPoll();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Execute a poll and schedule the next one adaptively.
|
|
125
|
+
*/
|
|
126
|
+
private executePollAndScheduleNext(): void {
|
|
127
|
+
if (!this.callbacks) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.callbacks
|
|
132
|
+
.poll()
|
|
133
|
+
.catch((error: unknown) => {
|
|
134
|
+
this.ctx.emit('job:error', { error: toError(error) });
|
|
135
|
+
})
|
|
136
|
+
.then(() => {
|
|
137
|
+
this.scheduleNextPoll();
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Schedule the next poll using adaptive timing.
|
|
143
|
+
*
|
|
144
|
+
* When change streams are active, uses `safetyPollInterval` (longer, safety net only).
|
|
145
|
+
* When change streams are inactive, uses `pollInterval` (shorter, primary discovery).
|
|
146
|
+
*/
|
|
147
|
+
private scheduleNextPoll(): void {
|
|
148
|
+
if (this.pollTimeoutId) {
|
|
149
|
+
clearTimeout(this.pollTimeoutId);
|
|
150
|
+
this.pollTimeoutId = null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!this.ctx.isRunning() || !this.callbacks) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const delay = this.callbacks.isChangeStreamActive()
|
|
158
|
+
? this.ctx.options.safetyPollInterval
|
|
159
|
+
: this.ctx.options.pollInterval;
|
|
160
|
+
|
|
161
|
+
this.pollTimeoutId = setTimeout(() => {
|
|
162
|
+
this.executePollAndScheduleNext();
|
|
163
|
+
}, delay);
|
|
164
|
+
}
|
|
165
|
+
|
|
111
166
|
/**
|
|
112
167
|
* Clean up old completed and failed jobs based on retention policy.
|
|
113
168
|
*
|
|
@@ -30,6 +30,7 @@ export interface ResolvedMonqueOptions
|
|
|
30
30
|
> {
|
|
31
31
|
// Ensure resolved options use the new naming convention
|
|
32
32
|
workerConcurrency: number;
|
|
33
|
+
safetyPollInterval: number;
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
36
|
* Shared context provided to all internal Monque services.
|
|
@@ -59,6 +60,9 @@ export interface SchedulerContext {
|
|
|
59
60
|
/** Type-safe event emitter */
|
|
60
61
|
emit: <K extends keyof MonqueEventMap>(event: K, payload: MonqueEventMap[K]) => boolean;
|
|
61
62
|
|
|
63
|
+
/** Notify the local scheduler about a pending job transition */
|
|
64
|
+
notifyPendingJob: (name: string, nextRunAt: Date) => void;
|
|
65
|
+
|
|
62
66
|
/** Convert MongoDB document to typed PersistedJob */
|
|
63
67
|
documentToPersistedJob: <T>(doc: WithId<Document>) => PersistedJob<T>;
|
|
64
68
|
}
|
package/src/scheduler/types.ts
CHANGED
|
@@ -196,4 +196,18 @@ export interface MonqueOptions {
|
|
|
196
196
|
* @default 5000
|
|
197
197
|
*/
|
|
198
198
|
statsCacheTtlMs?: number;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Interval in milliseconds between safety polls when change streams are active.
|
|
202
|
+
*
|
|
203
|
+
* When change streams are connected, the scheduler uses them as the primary
|
|
204
|
+
* notification mechanism and only polls at this longer interval as a safety net
|
|
205
|
+
* to catch any missed events. When change streams are unavailable, the scheduler
|
|
206
|
+
* falls back to the standard `pollInterval`.
|
|
207
|
+
*
|
|
208
|
+
* This is separate from `heartbeatInterval`, which updates job liveness signals.
|
|
209
|
+
*
|
|
210
|
+
* @default 30000 (30 seconds)
|
|
211
|
+
*/
|
|
212
|
+
safetyPollInterval?: number;
|
|
199
213
|
}
|