@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/jobExecutor.js
CHANGED
|
@@ -1,80 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
-
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);
|
|
24
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
-
function step(op) {
|
|
27
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
-
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;
|
|
30
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
-
switch (op[0]) {
|
|
32
|
-
case 0: case 1: t = op; break;
|
|
33
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
-
default:
|
|
37
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
-
if (t[2]) _.ops.pop();
|
|
42
|
-
_.trys.pop(); continue;
|
|
43
|
-
}
|
|
44
|
-
op = body.call(thisArg, _);
|
|
45
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
50
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
51
4
|
};
|
|
52
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
6
|
exports.executeJobById = exports.runClaimedJob = void 0;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
var isLiveLock = function (lockedAt, lockExpiry) {
|
|
66
|
-
return Boolean(lockedAt && lockedAt > lockExpiry);
|
|
67
|
-
};
|
|
68
|
-
var readClaimLock = function (job) {
|
|
7
|
+
const api_1 = require("@terreno/api");
|
|
8
|
+
const luxon_1 = require("luxon");
|
|
9
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
10
|
+
const activeJobExecution_1 = require("./activeJobExecution");
|
|
11
|
+
const claimLock_1 = require("./claimLock");
|
|
12
|
+
const job_1 = require("./models/job");
|
|
13
|
+
const retryBackoff_1 = require("./retryBackoff");
|
|
14
|
+
const TERMINAL_NOOP_STATUSES = new Set(["cancelled", "completed", "dead", "failed"]);
|
|
15
|
+
const buildLockExpiry = (lockTtlMs, now) => luxon_1.DateTime.fromJSDate(now, { zone: "utc" }).minus({ milliseconds: lockTtlMs }).toJSDate();
|
|
16
|
+
const isLiveLock = (lockedAt, lockExpiry) => Boolean(lockedAt && lockedAt > lockExpiry);
|
|
17
|
+
const readClaimLock = (job) => {
|
|
69
18
|
if (!job.lockedAt || !job.lockedBy) {
|
|
70
|
-
throw new Error(
|
|
19
|
+
throw new Error(`Job ${job._id.toString()} is missing claim lock metadata`);
|
|
71
20
|
}
|
|
72
21
|
return {
|
|
73
22
|
lockedAt: job.lockedAt,
|
|
74
23
|
lockedBy: job.lockedBy,
|
|
75
24
|
};
|
|
76
25
|
};
|
|
77
|
-
|
|
26
|
+
const classifyConflict = (job, lockExpiry, now) => {
|
|
78
27
|
if (job.runAt > now) {
|
|
79
28
|
return { kind: "conflict", reason: "not_due", runAt: job.runAt };
|
|
80
29
|
}
|
|
@@ -83,246 +32,178 @@ var classifyConflict = function (job, lockExpiry, now) {
|
|
|
83
32
|
}
|
|
84
33
|
return { kind: "conflict", reason: "locked" };
|
|
85
34
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
case 2:
|
|
99
|
-
current = _a.sent();
|
|
100
|
-
if ((current === null || current === void 0 ? void 0 : current.status) === "cancelled") {
|
|
101
|
-
return [2 /*return*/, current];
|
|
102
|
-
}
|
|
103
|
-
throw new Error("Failed to record success for job ".concat(jobId.toString()));
|
|
104
|
-
case 3: return [2 /*return*/, updated];
|
|
35
|
+
const recordHandlerSuccess = async (jobId, claim) => {
|
|
36
|
+
const updated = await job_1.Job.findOneAndUpdate({
|
|
37
|
+
...(0, claimLock_1.buildClaimOwnershipFilter)(jobId, claim),
|
|
38
|
+
status: "running",
|
|
39
|
+
}, {
|
|
40
|
+
$set: { status: "completed" },
|
|
41
|
+
$unset: { lastError: "" },
|
|
42
|
+
}, { returnDocument: "after" });
|
|
43
|
+
if (!updated) {
|
|
44
|
+
const current = await job_1.Job.findOneOrNone({ _id: jobId });
|
|
45
|
+
if (current?.status === "cancelled") {
|
|
46
|
+
return current;
|
|
105
47
|
}
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
48
|
+
throw new Error(`Failed to record success for job ${jobId.toString()}`);
|
|
49
|
+
}
|
|
50
|
+
return updated;
|
|
51
|
+
};
|
|
52
|
+
const recordMissingHandlerFailure = async (job, message, claim) => {
|
|
53
|
+
const updated = await job_1.Job.findOneAndUpdate((0, claimLock_1.buildClaimOwnershipFilter)(job._id, claim), {
|
|
54
|
+
$set: {
|
|
55
|
+
lastError: message,
|
|
56
|
+
status: "failed",
|
|
57
|
+
},
|
|
58
|
+
}, { returnDocument: "after" });
|
|
59
|
+
if (!updated) {
|
|
60
|
+
throw new Error(`Failed to record missing handler for job ${job._id.toString()}`);
|
|
61
|
+
}
|
|
62
|
+
return updated;
|
|
63
|
+
};
|
|
64
|
+
const recordHandlerFailure = async ({ claim, error, job, signal, }) => {
|
|
65
|
+
if (signal.aborted && (0, claimLock_1.isAbortError)(error)) {
|
|
66
|
+
const current = await job_1.Job.findOneOrNone({ _id: job._id });
|
|
67
|
+
if (current?.status === "cancelled") {
|
|
68
|
+
return current;
|
|
124
69
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
switch (_c.label) {
|
|
132
|
-
case 0:
|
|
133
|
-
if (!(signal.aborted && (0, claimLock_1.isAbortError)(error))) return [3 /*break*/, 3];
|
|
134
|
-
return [4 /*yield*/, job_1.Job.findOneOrNone({ _id: job._id })];
|
|
135
|
-
case 1:
|
|
136
|
-
current = _c.sent();
|
|
137
|
-
if ((current === null || current === void 0 ? void 0 : current.status) === "cancelled") {
|
|
138
|
-
return [2 /*return*/, current];
|
|
139
|
-
}
|
|
140
|
-
return [4 /*yield*/, job_1.Job.findOneAndUpdate((0, claimLock_1.buildClaimOwnershipFilter)(job._id, claim), {
|
|
141
|
-
$set: { status: "pending" },
|
|
142
|
-
$unset: { lockedAt: "", lockedBy: "" },
|
|
143
|
-
}, { returnDocument: "after" })];
|
|
144
|
-
case 2:
|
|
145
|
-
released = _c.sent();
|
|
146
|
-
if (!released) {
|
|
147
|
-
throw new Error("Failed to release lock for job ".concat(job._id.toString()));
|
|
148
|
-
}
|
|
149
|
-
return [2 /*return*/, released];
|
|
150
|
-
case 3:
|
|
151
|
-
errorMessage = error instanceof Error ? error.message : String(error);
|
|
152
|
-
errorClass = error instanceof Error ? error.name : "Error";
|
|
153
|
-
attemptedAt = luxon_1.DateTime.utc().toJSDate();
|
|
154
|
-
attemptEntry = {
|
|
155
|
-
at: attemptedAt,
|
|
156
|
-
error: errorMessage,
|
|
157
|
-
errorClass: errorClass,
|
|
158
|
-
};
|
|
159
|
-
nextAttemptCount = job.attemptCount + 1;
|
|
160
|
-
ownershipFilter = (0, claimLock_1.buildClaimOwnershipFilter)(job._id, claim);
|
|
161
|
-
if (!(nextAttemptCount >= job.maxAttempts)) return [3 /*break*/, 5];
|
|
162
|
-
return [4 /*yield*/, job_1.Job.findOneAndUpdate(ownershipFilter, {
|
|
163
|
-
$push: { attempts: attemptEntry },
|
|
164
|
-
$set: {
|
|
165
|
-
attemptCount: nextAttemptCount,
|
|
166
|
-
lastError: errorMessage,
|
|
167
|
-
status: "dead",
|
|
168
|
-
},
|
|
169
|
-
$unset: { lockedAt: "", lockedBy: "" },
|
|
170
|
-
}, { returnDocument: "after" })];
|
|
171
|
-
case 4:
|
|
172
|
-
dead = _c.sent();
|
|
173
|
-
if (!dead) {
|
|
174
|
-
throw new Error("Failed to record dead status for job ".concat(job._id.toString()));
|
|
175
|
-
}
|
|
176
|
-
return [2 /*return*/, dead];
|
|
177
|
-
case 5:
|
|
178
|
-
runAt = (0, retryBackoff_1.computeRetryRunAt)({
|
|
179
|
-
attemptCount: nextAttemptCount,
|
|
180
|
-
backoffMs: job.backoffMs,
|
|
181
|
-
from: luxon_1.DateTime.utc(),
|
|
182
|
-
maxBackoffMs: job.maxBackoffMs,
|
|
183
|
-
}).toJSDate();
|
|
184
|
-
return [4 /*yield*/, job_1.Job.findOneAndUpdate(ownershipFilter, {
|
|
185
|
-
$push: { attempts: attemptEntry },
|
|
186
|
-
$set: {
|
|
187
|
-
attemptCount: nextAttemptCount,
|
|
188
|
-
lastError: errorMessage,
|
|
189
|
-
runAt: runAt,
|
|
190
|
-
status: "pending",
|
|
191
|
-
},
|
|
192
|
-
$unset: { lockedAt: "", lockedBy: "" },
|
|
193
|
-
}, { returnDocument: "after" })];
|
|
194
|
-
case 6:
|
|
195
|
-
pending = _c.sent();
|
|
196
|
-
if (!pending) {
|
|
197
|
-
throw new Error("Failed to record retry for job ".concat(job._id.toString()));
|
|
198
|
-
}
|
|
199
|
-
return [2 /*return*/, pending];
|
|
70
|
+
const released = await job_1.Job.findOneAndUpdate((0, claimLock_1.buildClaimOwnershipFilter)(job._id, claim), {
|
|
71
|
+
$set: { status: "pending" },
|
|
72
|
+
$unset: { lockedAt: "", lockedBy: "" },
|
|
73
|
+
}, { returnDocument: "after" });
|
|
74
|
+
if (!released) {
|
|
75
|
+
throw new Error(`Failed to release lock for job ${job._id.toString()}`);
|
|
200
76
|
}
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
case 0: return [4 /*yield*/, definition.handler(job.payload, {
|
|
226
|
-
jobId: job._id.toString(),
|
|
227
|
-
log: log,
|
|
228
|
-
signal: executionSignal,
|
|
229
|
-
})];
|
|
230
|
-
case 1:
|
|
231
|
-
_a.sent();
|
|
232
|
-
return [2 /*return*/];
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
}); })];
|
|
236
|
-
case 2:
|
|
237
|
-
_c.sent();
|
|
238
|
-
return [2 /*return*/, recordHandlerSuccess(job._id, claim)];
|
|
239
|
-
case 3:
|
|
240
|
-
error_1 = _c.sent();
|
|
241
|
-
return [2 /*return*/, recordHandlerFailure({ claim: claim, error: error_1, job: job, signal: executionSignal })];
|
|
242
|
-
case 4:
|
|
243
|
-
(0, activeJobExecution_1.endActiveJobExecution)(job._id.toString());
|
|
244
|
-
return [7 /*endfinally*/];
|
|
245
|
-
case 5: return [2 /*return*/];
|
|
77
|
+
return released;
|
|
78
|
+
}
|
|
79
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
80
|
+
const errorClass = error instanceof Error ? error.name : "Error";
|
|
81
|
+
const attemptedAt = luxon_1.DateTime.utc().toJSDate();
|
|
82
|
+
const attemptEntry = {
|
|
83
|
+
at: attemptedAt,
|
|
84
|
+
error: errorMessage,
|
|
85
|
+
errorClass,
|
|
86
|
+
};
|
|
87
|
+
const nextAttemptCount = job.attemptCount + 1;
|
|
88
|
+
const ownershipFilter = (0, claimLock_1.buildClaimOwnershipFilter)(job._id, claim);
|
|
89
|
+
if (nextAttemptCount >= job.maxAttempts) {
|
|
90
|
+
const dead = await job_1.Job.findOneAndUpdate(ownershipFilter, {
|
|
91
|
+
$push: { attempts: attemptEntry },
|
|
92
|
+
$set: {
|
|
93
|
+
attemptCount: nextAttemptCount,
|
|
94
|
+
lastError: errorMessage,
|
|
95
|
+
status: "dead",
|
|
96
|
+
},
|
|
97
|
+
$unset: { lockedAt: "", lockedBy: "" },
|
|
98
|
+
}, { returnDocument: "after" });
|
|
99
|
+
if (!dead) {
|
|
100
|
+
throw new Error(`Failed to record dead status for job ${job._id.toString()}`);
|
|
246
101
|
}
|
|
102
|
+
return dead;
|
|
103
|
+
}
|
|
104
|
+
const runAt = (0, retryBackoff_1.computeRetryRunAt)({
|
|
105
|
+
attemptCount: nextAttemptCount,
|
|
106
|
+
backoffMs: job.backoffMs,
|
|
107
|
+
from: luxon_1.DateTime.utc(),
|
|
108
|
+
maxBackoffMs: job.maxBackoffMs,
|
|
109
|
+
}).toJSDate();
|
|
110
|
+
const pending = await job_1.Job.findOneAndUpdate(ownershipFilter, {
|
|
111
|
+
$push: { attempts: attemptEntry },
|
|
112
|
+
$set: {
|
|
113
|
+
attemptCount: nextAttemptCount,
|
|
114
|
+
lastError: errorMessage,
|
|
115
|
+
runAt,
|
|
116
|
+
status: "pending",
|
|
117
|
+
},
|
|
118
|
+
$unset: { lockedAt: "", lockedBy: "" },
|
|
119
|
+
}, { returnDocument: "after" });
|
|
120
|
+
if (!pending) {
|
|
121
|
+
throw new Error(`Failed to record retry for job ${job._id.toString()}`);
|
|
122
|
+
}
|
|
123
|
+
return pending;
|
|
124
|
+
};
|
|
125
|
+
const runClaimedJob = async ({ claim, host, job, signal, }) => {
|
|
126
|
+
const definition = host.getDefinition(job.name);
|
|
127
|
+
if (!definition) {
|
|
128
|
+
const message = `No handler registered for job name "${job.name}"`;
|
|
129
|
+
return recordMissingHandlerFailure(job, message, claim);
|
|
130
|
+
}
|
|
131
|
+
const log = (0, api_1.createScopedLogger)({
|
|
132
|
+
labels: { jobId: job._id.toString(), jobName: job.name },
|
|
133
|
+
prefix: "[Job]",
|
|
247
134
|
});
|
|
248
|
-
|
|
135
|
+
const executionSignal = (0, activeJobExecution_1.beginActiveJobExecution)(job._id.toString(), signal);
|
|
136
|
+
try {
|
|
137
|
+
await (0, api_1.runWithRequestContext)({ jobId: job._id.toString() }, async () => {
|
|
138
|
+
await definition.handler(job.payload, {
|
|
139
|
+
jobId: job._id.toString(),
|
|
140
|
+
log,
|
|
141
|
+
signal: executionSignal,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
return recordHandlerSuccess(job._id, claim);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return recordHandlerFailure({ claim, error, job, signal: executionSignal });
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
(0, activeJobExecution_1.endActiveJobExecution)(job._id.toString());
|
|
151
|
+
}
|
|
152
|
+
};
|
|
249
153
|
exports.runClaimedJob = runClaimedJob;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
154
|
+
const claimJobById = async ({ host, jobId, now = luxon_1.DateTime.utc().toJSDate(), }) => {
|
|
155
|
+
const lockExpiry = buildLockExpiry(host.getLockTtlMs(), now);
|
|
156
|
+
const claim = (0, claimLock_1.createClaimLock)(now);
|
|
157
|
+
const claimed = await job_1.Job.findOneAndUpdate({
|
|
158
|
+
_id: jobId,
|
|
159
|
+
$or: [{ lockedAt: { $exists: false } }, { lockedAt: null }, { lockedAt: { $lte: lockExpiry } }],
|
|
160
|
+
runAt: { $lte: now },
|
|
161
|
+
status: { $in: ["pending", "running", "scheduled"] },
|
|
162
|
+
}, {
|
|
163
|
+
$set: {
|
|
164
|
+
lockedAt: claim.lockedAt,
|
|
165
|
+
lockedBy: claim.lockedBy,
|
|
166
|
+
status: "running",
|
|
167
|
+
},
|
|
168
|
+
}, { returnDocument: "after" });
|
|
169
|
+
return claimed ?? undefined;
|
|
170
|
+
};
|
|
171
|
+
const executeJobById = async ({ host, jobId, signal, }) => {
|
|
172
|
+
if (!mongoose_1.default.isValidObjectId(jobId)) {
|
|
173
|
+
return { kind: "not_found" };
|
|
174
|
+
}
|
|
175
|
+
const existing = await job_1.Job.findOneOrNone({ _id: jobId });
|
|
176
|
+
if (!existing) {
|
|
177
|
+
return { kind: "not_found" };
|
|
178
|
+
}
|
|
179
|
+
const now = luxon_1.DateTime.utc().toJSDate();
|
|
180
|
+
const lockExpiry = buildLockExpiry(host.getLockTtlMs(), now);
|
|
181
|
+
if (TERMINAL_NOOP_STATUSES.has(existing.status)) {
|
|
182
|
+
return { job: existing, kind: "noop" };
|
|
183
|
+
}
|
|
184
|
+
if (existing.runAt > now) {
|
|
185
|
+
return { kind: "conflict", reason: "not_due", runAt: existing.runAt };
|
|
186
|
+
}
|
|
187
|
+
if (existing.status === "running" && isLiveLock(existing.lockedAt, lockExpiry)) {
|
|
188
|
+
return { kind: "conflict", reason: "locked" };
|
|
189
|
+
}
|
|
190
|
+
const claimed = await claimJobById({ host, jobId, now });
|
|
191
|
+
if (!claimed) {
|
|
192
|
+
const refreshed = await job_1.Job.findOneOrNone({ _id: jobId });
|
|
193
|
+
if (!refreshed) {
|
|
194
|
+
return { kind: "not_found" };
|
|
273
195
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
var executeJobById = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
|
277
|
-
var existing, now, lockExpiry, claimed, refreshed, executed;
|
|
278
|
-
var host = _b.host, jobId = _b.jobId, signal = _b.signal;
|
|
279
|
-
return __generator(this, function (_c) {
|
|
280
|
-
switch (_c.label) {
|
|
281
|
-
case 0:
|
|
282
|
-
if (!mongoose_1.default.isValidObjectId(jobId)) {
|
|
283
|
-
return [2 /*return*/, { kind: "not_found" }];
|
|
284
|
-
}
|
|
285
|
-
return [4 /*yield*/, job_1.Job.findOneOrNone({ _id: jobId })];
|
|
286
|
-
case 1:
|
|
287
|
-
existing = _c.sent();
|
|
288
|
-
if (!existing) {
|
|
289
|
-
return [2 /*return*/, { kind: "not_found" }];
|
|
290
|
-
}
|
|
291
|
-
now = luxon_1.DateTime.utc().toJSDate();
|
|
292
|
-
lockExpiry = buildLockExpiry(host.getLockTtlMs(), now);
|
|
293
|
-
if (TERMINAL_NOOP_STATUSES.has(existing.status)) {
|
|
294
|
-
return [2 /*return*/, { job: existing, kind: "noop" }];
|
|
295
|
-
}
|
|
296
|
-
if (existing.runAt > now) {
|
|
297
|
-
return [2 /*return*/, { kind: "conflict", reason: "not_due", runAt: existing.runAt }];
|
|
298
|
-
}
|
|
299
|
-
if (existing.status === "running" && isLiveLock(existing.lockedAt, lockExpiry)) {
|
|
300
|
-
return [2 /*return*/, { kind: "conflict", reason: "locked" }];
|
|
301
|
-
}
|
|
302
|
-
return [4 /*yield*/, claimJobById({ host: host, jobId: jobId, now: now })];
|
|
303
|
-
case 2:
|
|
304
|
-
claimed = _c.sent();
|
|
305
|
-
if (!!claimed) return [3 /*break*/, 4];
|
|
306
|
-
return [4 /*yield*/, job_1.Job.findOneOrNone({ _id: jobId })];
|
|
307
|
-
case 3:
|
|
308
|
-
refreshed = _c.sent();
|
|
309
|
-
if (!refreshed) {
|
|
310
|
-
return [2 /*return*/, { kind: "not_found" }];
|
|
311
|
-
}
|
|
312
|
-
if (TERMINAL_NOOP_STATUSES.has(refreshed.status)) {
|
|
313
|
-
return [2 /*return*/, { job: refreshed, kind: "noop" }];
|
|
314
|
-
}
|
|
315
|
-
return [2 /*return*/, classifyConflict(refreshed, lockExpiry, now)];
|
|
316
|
-
case 4: return [4 /*yield*/, (0, exports.runClaimedJob)({
|
|
317
|
-
claim: readClaimLock(claimed),
|
|
318
|
-
host: host,
|
|
319
|
-
job: claimed,
|
|
320
|
-
signal: signal,
|
|
321
|
-
})];
|
|
322
|
-
case 5:
|
|
323
|
-
executed = _c.sent();
|
|
324
|
-
return [2 /*return*/, { job: executed, kind: "executed" }];
|
|
196
|
+
if (TERMINAL_NOOP_STATUSES.has(refreshed.status)) {
|
|
197
|
+
return { job: refreshed, kind: "noop" };
|
|
325
198
|
}
|
|
199
|
+
return classifyConflict(refreshed, lockExpiry, now);
|
|
200
|
+
}
|
|
201
|
+
const executed = await (0, exports.runClaimedJob)({
|
|
202
|
+
claim: readClaimLock(claimed),
|
|
203
|
+
host,
|
|
204
|
+
job: claimed,
|
|
205
|
+
signal,
|
|
326
206
|
});
|
|
327
|
-
|
|
207
|
+
return { job: executed, kind: "executed" };
|
|
208
|
+
};
|
|
328
209
|
exports.executeJobById = executeJobById;
|