@terreno/jobs 57.4.0 → 57.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/README.md +3 -3
- package/dist/activeJobExecution.js +7 -7
- package/dist/claimLock.js +12 -14
- package/dist/dispatchCompensation.js +29 -83
- package/dist/dispatchError.js +12 -28
- package/dist/jobExecutor.js +178 -297
- package/dist/jobsApp.js +124 -234
- package/dist/jobsService.js +261 -461
- package/dist/models/job.js +9 -9
- package/dist/models/jobSchedule.js +4 -4
- package/dist/retryBackoff.js +11 -14
- package/dist/routes/jobsAdmin.js +189 -370
- package/dist/routes/jobsExecute.js +41 -101
- package/dist/runners/gcpCloudTasks.js +116 -200
- package/dist/runners/mongoRunner.js +90 -184
- package/dist/runners/vercelQueues.js +105 -194
- package/dist/scheduleCron.js +18 -27
- package/dist/scheduler.js +68 -170
- package/package.json +4 -4
- package/src/models/job.ts +4 -4
- package/src/models/jobSchedule.ts +3 -2
- package/src/tests/packageExports.test.ts +19 -3
- package/tsconfig.json +6 -21
package/dist/jobsApp.js
CHANGED
|
@@ -1,50 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
3
|
exports.JobsApp = void 0;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
4
|
+
const api_1 = require("@terreno/api");
|
|
5
|
+
const jobExecutor_1 = require("./jobExecutor");
|
|
6
|
+
const jobsService_1 = require("./jobsService");
|
|
7
|
+
const job_1 = require("./models/job");
|
|
8
|
+
const jobSchedule_1 = require("./models/jobSchedule");
|
|
9
|
+
const jobsAdmin_1 = require("./routes/jobsAdmin");
|
|
10
|
+
const jobsExecute_1 = require("./routes/jobsExecute");
|
|
11
|
+
const mongoRunner_1 = require("./runners/mongoRunner");
|
|
48
12
|
/**
|
|
49
13
|
* TerrenoPlugin for durable background jobs.
|
|
50
14
|
*
|
|
@@ -52,100 +16,71 @@ var mongoRunner_1 = require("./runners/mongoRunner");
|
|
|
52
16
|
* start a worker poll loop. Call {@link JobsApp.startWorker} explicitly from the
|
|
53
17
|
* API process or a dedicated worker entrypoint.
|
|
54
18
|
*/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
19
|
+
class JobsApp {
|
|
20
|
+
options;
|
|
21
|
+
runner;
|
|
22
|
+
service;
|
|
23
|
+
abortController;
|
|
24
|
+
workerActive = false;
|
|
25
|
+
workerPromise;
|
|
26
|
+
workerStartPromise;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options ?? {};
|
|
29
|
+
this.runner = this.options.runner ?? new mongoRunner_1.MongoJobRunner();
|
|
61
30
|
this.service = new jobsService_1.JobsService({
|
|
62
|
-
defaultTimezone:
|
|
31
|
+
defaultTimezone: this.options.timezone ?? "UTC",
|
|
63
32
|
lockTtlMs: this.options.lockTtlMs,
|
|
64
33
|
runner: this.runner,
|
|
65
34
|
});
|
|
66
35
|
}
|
|
67
|
-
|
|
36
|
+
define(name, definition) {
|
|
68
37
|
this.service.define(name, definition);
|
|
69
38
|
if (this.workerActive && definition.schedule) {
|
|
70
39
|
this.service.markSchedulesDirty();
|
|
71
40
|
}
|
|
72
41
|
return this;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
JobsApp.prototype.getDefinition = function (name) {
|
|
42
|
+
}
|
|
43
|
+
getBasePath() {
|
|
44
|
+
return this.options.basePath ?? "/jobs";
|
|
45
|
+
}
|
|
46
|
+
getDefinition(name) {
|
|
79
47
|
return this.service.getDefinition(name);
|
|
80
|
-
}
|
|
81
|
-
|
|
48
|
+
}
|
|
49
|
+
getDefaultTimezone() {
|
|
82
50
|
return this.service.getDefaultTimezone();
|
|
83
|
-
}
|
|
84
|
-
|
|
51
|
+
}
|
|
52
|
+
getLockTtlMs() {
|
|
85
53
|
return this.service.getLockTtlMs();
|
|
86
|
-
}
|
|
54
|
+
}
|
|
87
55
|
/** Execute a persisted job by id from an external dispatch surface (queue consumer, execute HTTP). */
|
|
88
|
-
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
host: this,
|
|
94
|
-
jobId: jobId,
|
|
95
|
-
signal: signal,
|
|
96
|
-
})];
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
JobsApp.prototype.getPollIntervalMs = function () {
|
|
101
|
-
var _a;
|
|
102
|
-
return (_a = this.options.pollIntervalMs) !== null && _a !== void 0 ? _a : 1000;
|
|
103
|
-
};
|
|
104
|
-
JobsApp.prototype.reconcileSchedules = function () {
|
|
105
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
106
|
-
return __generator(this, function (_a) {
|
|
107
|
-
switch (_a.label) {
|
|
108
|
-
case 0: return [4 /*yield*/, this.service.reconcileSchedules()];
|
|
109
|
-
case 1:
|
|
110
|
-
_a.sent();
|
|
111
|
-
return [2 /*return*/];
|
|
112
|
-
}
|
|
113
|
-
});
|
|
56
|
+
async executeQueuedJob(jobId, signal = AbortSignal.timeout(30 * 60 * 1_000)) {
|
|
57
|
+
return (0, jobExecutor_1.executeJobById)({
|
|
58
|
+
host: this,
|
|
59
|
+
jobId,
|
|
60
|
+
signal,
|
|
114
61
|
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
130
|
-
return __generator(this, function (_a) {
|
|
131
|
-
switch (_a.label) {
|
|
132
|
-
case 0: return [4 /*yield*/, this.service.tickSchedules(now)];
|
|
133
|
-
case 1:
|
|
134
|
-
_a.sent();
|
|
135
|
-
return [2 /*return*/];
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
JobsApp.prototype.register = function (app, openApi) {
|
|
62
|
+
}
|
|
63
|
+
getPollIntervalMs() {
|
|
64
|
+
return this.options.pollIntervalMs ?? 1_000;
|
|
65
|
+
}
|
|
66
|
+
async reconcileSchedules() {
|
|
67
|
+
await this.service.reconcileSchedules();
|
|
68
|
+
}
|
|
69
|
+
async reconcileSchedulesIfDirty() {
|
|
70
|
+
await this.service.reconcileSchedulesIfDirty();
|
|
71
|
+
}
|
|
72
|
+
async tickSchedules(now) {
|
|
73
|
+
await this.service.tickSchedules(now);
|
|
74
|
+
}
|
|
75
|
+
register(app, openApi) {
|
|
141
76
|
(0, jobsService_1.registerJobsService)(this.service);
|
|
142
77
|
(0, jobsAdmin_1.registerJobsAdminRoutes)({
|
|
143
|
-
app
|
|
78
|
+
app,
|
|
144
79
|
options: {
|
|
145
80
|
accessControl: this.options.accessControl,
|
|
146
81
|
basePath: this.getBasePath(),
|
|
147
82
|
jobsService: this.service,
|
|
148
|
-
openApi
|
|
83
|
+
openApi,
|
|
149
84
|
redactPayload: this.options.redactPayload,
|
|
150
85
|
},
|
|
151
86
|
});
|
|
@@ -154,7 +89,7 @@ var JobsApp = /** @class */ (function () {
|
|
|
154
89
|
throw new Error("JobsApp execute route is enabled but executeAuth is missing. Provide executeAuth when mountExecuteRoute is true or the runner requires the execute route.");
|
|
155
90
|
}
|
|
156
91
|
(0, jobsExecute_1.registerJobsExecuteRoute)({
|
|
157
|
-
app
|
|
92
|
+
app,
|
|
158
93
|
options: {
|
|
159
94
|
basePath: this.getBasePath(),
|
|
160
95
|
executeAuth: this.options.executeAuth,
|
|
@@ -162,14 +97,14 @@ var JobsApp = /** @class */ (function () {
|
|
|
162
97
|
},
|
|
163
98
|
});
|
|
164
99
|
}
|
|
165
|
-
void job_1.Job.init().catch(
|
|
166
|
-
api_1.logger.error(
|
|
100
|
+
void job_1.Job.init().catch((error) => {
|
|
101
|
+
api_1.logger.error(`[jobs] Failed to build Job indexes: ${String(error)}`);
|
|
167
102
|
});
|
|
168
|
-
void jobSchedule_1.JobSchedule.init().catch(
|
|
169
|
-
api_1.logger.error(
|
|
103
|
+
void jobSchedule_1.JobSchedule.init().catch((error) => {
|
|
104
|
+
api_1.logger.error(`[jobs] Failed to build JobSchedule indexes: ${String(error)}`);
|
|
170
105
|
});
|
|
171
|
-
}
|
|
172
|
-
|
|
106
|
+
}
|
|
107
|
+
adminContribution() {
|
|
173
108
|
return {
|
|
174
109
|
customScreens: [
|
|
175
110
|
{
|
|
@@ -180,129 +115,84 @@ var JobsApp = /** @class */ (function () {
|
|
|
180
115
|
],
|
|
181
116
|
homeWidgets: [{ displayName: "Jobs", icon: "clock", id: "jobs" }],
|
|
182
117
|
};
|
|
183
|
-
}
|
|
184
|
-
|
|
118
|
+
}
|
|
119
|
+
shouldMountExecuteRoute() {
|
|
185
120
|
if (this.options.mountExecuteRoute) {
|
|
186
121
|
return true;
|
|
187
122
|
}
|
|
188
123
|
return this.runner.requiresExecuteRoute === true;
|
|
189
|
-
}
|
|
190
|
-
|
|
124
|
+
}
|
|
125
|
+
isWorkerActive() {
|
|
191
126
|
return this.workerActive;
|
|
192
|
-
}
|
|
127
|
+
}
|
|
193
128
|
/**
|
|
194
129
|
* Starts the background worker poll loop without awaiting it.
|
|
195
130
|
*
|
|
196
131
|
* Fire-and-forget: resolves once the runner has been scheduled. Runner errors are
|
|
197
132
|
* logged and worker state is reset; they are not thrown from this method.
|
|
198
133
|
*/
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
var stopPromise, _a;
|
|
232
|
-
var _b, _c, _d;
|
|
233
|
-
return __generator(this, function (_e) {
|
|
234
|
-
switch (_e.label) {
|
|
235
|
-
case 0:
|
|
236
|
-
if (!this.workerActive) {
|
|
237
|
-
return [2 /*return*/];
|
|
238
|
-
}
|
|
239
|
-
stopPromise = (_c = (_b = this.runner).stop) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
240
|
-
(_d = this.abortController) === null || _d === void 0 ? void 0 : _d.abort();
|
|
241
|
-
_e.label = 1;
|
|
242
|
-
case 1:
|
|
243
|
-
_e.trys.push([1, 3, , 4]);
|
|
244
|
-
return [4 /*yield*/, this.workerPromise];
|
|
245
|
-
case 2:
|
|
246
|
-
_e.sent();
|
|
247
|
-
return [3 /*break*/, 4];
|
|
248
|
-
case 3:
|
|
249
|
-
_a = _e.sent();
|
|
250
|
-
return [3 /*break*/, 4];
|
|
251
|
-
case 4: return [4 /*yield*/, stopPromise];
|
|
252
|
-
case 5:
|
|
253
|
-
_e.sent();
|
|
254
|
-
this.resetWorkerState();
|
|
255
|
-
return [2 /*return*/];
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
};
|
|
260
|
-
JobsApp.prototype.resetWorkerState = function () {
|
|
134
|
+
async startWorker() {
|
|
135
|
+
if (this.workerActive) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (this.workerStartPromise !== undefined) {
|
|
139
|
+
await this.workerStartPromise;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this.workerStartPromise = this.beginWorkerStart();
|
|
143
|
+
try {
|
|
144
|
+
await this.workerStartPromise;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
this.workerStartPromise = undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async stopWorker() {
|
|
151
|
+
if (!this.workerActive) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const stopPromise = this.runner.stop?.();
|
|
155
|
+
this.abortController?.abort();
|
|
156
|
+
try {
|
|
157
|
+
await this.workerPromise;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// startWorker is fire-and-forget; runner errors are logged in workerPromise.catch.
|
|
161
|
+
}
|
|
162
|
+
await stopPromise;
|
|
163
|
+
this.resetWorkerState();
|
|
164
|
+
}
|
|
165
|
+
resetWorkerState() {
|
|
261
166
|
this.workerActive = false;
|
|
262
167
|
this.abortController = undefined;
|
|
263
168
|
this.workerPromise = undefined;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
this.abortController = new AbortController();
|
|
286
|
-
this.workerPromise = this.runner
|
|
287
|
-
.start({
|
|
288
|
-
jobs: this,
|
|
289
|
-
pollIntervalMs: this.getPollIntervalMs(),
|
|
290
|
-
signal: this.abortController.signal,
|
|
291
|
-
})
|
|
292
|
-
.catch(function (error) {
|
|
293
|
-
_this.resetWorkerState();
|
|
294
|
-
api_1.logger.error("[jobs] Worker stopped with error: ".concat(String(error)));
|
|
295
|
-
});
|
|
296
|
-
return [3 /*break*/, 4];
|
|
297
|
-
case 3:
|
|
298
|
-
error_1 = _a.sent();
|
|
299
|
-
this.resetWorkerState();
|
|
300
|
-
throw error_1;
|
|
301
|
-
case 4: return [2 /*return*/];
|
|
302
|
-
}
|
|
169
|
+
}
|
|
170
|
+
async beginWorkerStart() {
|
|
171
|
+
if (this.workerActive) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!this.runner.start) {
|
|
175
|
+
throw new Error(`Job runner "${this.runner.id}" does not support start()`);
|
|
176
|
+
}
|
|
177
|
+
this.workerActive = true;
|
|
178
|
+
try {
|
|
179
|
+
await this.service.reconcileSchedules();
|
|
180
|
+
this.abortController = new AbortController();
|
|
181
|
+
this.workerPromise = this.runner
|
|
182
|
+
.start({
|
|
183
|
+
jobs: this,
|
|
184
|
+
pollIntervalMs: this.getPollIntervalMs(),
|
|
185
|
+
signal: this.abortController.signal,
|
|
186
|
+
})
|
|
187
|
+
.catch((error) => {
|
|
188
|
+
this.resetWorkerState();
|
|
189
|
+
api_1.logger.error(`[jobs] Worker stopped with error: ${String(error)}`);
|
|
303
190
|
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
this.resetWorkerState();
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
308
198
|
exports.JobsApp = JobsApp;
|