@terreno/jobs 57.4.0 → 57.6.1
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 +6 -6
- 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/README.md
CHANGED
|
@@ -47,10 +47,10 @@ process or a dedicated worker entrypoint.
|
|
|
47
47
|
|
|
48
48
|
## Documentation
|
|
49
49
|
|
|
50
|
-
Full API reference: [docs/reference/jobs.md](https://github.com/
|
|
50
|
+
Full API reference: [docs/reference/jobs.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/reference/jobs.md)
|
|
51
51
|
|
|
52
|
-
Operator guide: [docs/how-to/background-jobs.md](https://github.com/
|
|
52
|
+
Operator guide: [docs/how-to/background-jobs.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/how-to/background-jobs.md)
|
|
53
53
|
|
|
54
54
|
## License and Contributing
|
|
55
55
|
|
|
56
|
-
Licensed under the [MIT License](https://github.com/
|
|
56
|
+
Licensed under the [MIT License](https://github.com/TerrenoLabs/terreno/blob/master/LICENSE). See [CONTRIBUTING.md](https://github.com/TerrenoLabs/terreno/blob/master/CONTRIBUTING.md) for contribution guidelines.
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.abortActiveJobExecution = exports.endActiveJobExecution = exports.beginActiveJobExecution = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const activeExecutions = new Map();
|
|
5
|
+
const beginActiveJobExecution = (jobId, parentSignal) => {
|
|
6
|
+
const controller = new AbortController();
|
|
7
7
|
activeExecutions.set(jobId, controller);
|
|
8
8
|
if (parentSignal.aborted) {
|
|
9
9
|
controller.abort();
|
|
10
10
|
}
|
|
11
11
|
else {
|
|
12
|
-
parentSignal.addEventListener("abort",
|
|
12
|
+
parentSignal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
13
13
|
}
|
|
14
14
|
return controller.signal;
|
|
15
15
|
};
|
|
16
16
|
exports.beginActiveJobExecution = beginActiveJobExecution;
|
|
17
|
-
|
|
17
|
+
const endActiveJobExecution = (jobId) => {
|
|
18
18
|
activeExecutions.delete(jobId);
|
|
19
19
|
};
|
|
20
20
|
exports.endActiveJobExecution = endActiveJobExecution;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const abortActiveJobExecution = (jobId) => {
|
|
22
|
+
const controller = activeExecutions.get(jobId);
|
|
23
23
|
if (!controller) {
|
|
24
24
|
return false;
|
|
25
25
|
}
|
package/dist/claimLock.js
CHANGED
|
@@ -4,31 +4,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.isAbortError = exports.hasWorkerIdPrefix = exports.buildClaimOwnershipFilter = exports.createClaimLock = exports.getWorkerId = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
9
|
/** @internal */
|
|
10
|
-
|
|
10
|
+
const getWorkerId = () => `${node_os_1.default.hostname()}:${process.pid}`;
|
|
11
11
|
exports.getWorkerId = getWorkerId;
|
|
12
|
-
|
|
13
|
-
lockedAt
|
|
14
|
-
lockedBy:
|
|
15
|
-
});
|
|
12
|
+
const createClaimLock = (lockedAt) => ({
|
|
13
|
+
lockedAt,
|
|
14
|
+
lockedBy: `${(0, exports.getWorkerId)()}:${(0, node_crypto_1.randomUUID)()}`,
|
|
15
|
+
});
|
|
16
16
|
exports.createClaimLock = createClaimLock;
|
|
17
|
-
|
|
17
|
+
const buildClaimOwnershipFilter = (jobId, claim) => ({
|
|
18
18
|
_id: jobId,
|
|
19
19
|
lockedAt: claim.lockedAt,
|
|
20
20
|
lockedBy: claim.lockedBy,
|
|
21
|
-
});
|
|
21
|
+
});
|
|
22
22
|
exports.buildClaimOwnershipFilter = buildClaimOwnershipFilter;
|
|
23
23
|
/** @internal */
|
|
24
|
-
|
|
24
|
+
const hasWorkerIdPrefix = (lockedBy) => {
|
|
25
25
|
if (!lockedBy) {
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
|
-
return lockedBy.startsWith(
|
|
28
|
+
return lockedBy.startsWith(`${(0, exports.getWorkerId)()}:`);
|
|
29
29
|
};
|
|
30
30
|
exports.hasWorkerIdPrefix = hasWorkerIdPrefix;
|
|
31
|
-
|
|
32
|
-
return error instanceof Error && error.name === "AbortError";
|
|
33
|
-
};
|
|
31
|
+
const isAbortError = (error) => error instanceof Error && error.name === "AbortError";
|
|
34
32
|
exports.isAbortError = isAbortError;
|
|
@@ -1,89 +1,35 @@
|
|
|
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.dispatchJobToRunner = void 0;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
],
|
|
53
|
-
attemptCount: 0,
|
|
54
|
-
status: "pending",
|
|
55
|
-
})];
|
|
56
|
-
case 1:
|
|
57
|
-
removed = _a.sent();
|
|
58
|
-
return [2 /*return*/, removed !== null];
|
|
59
|
-
}
|
|
4
|
+
const api_1 = require("@terreno/api");
|
|
5
|
+
const dispatchError_1 = require("./dispatchError");
|
|
6
|
+
const job_1 = require("./models/job");
|
|
7
|
+
const removeUntouchedPendingJob = async (jobId) => {
|
|
8
|
+
const removed = await job_1.Job.findOneAndDelete({
|
|
9
|
+
_id: jobId,
|
|
10
|
+
$and: [
|
|
11
|
+
{ $or: [{ lockedAt: { $exists: false } }, { lockedAt: null }] },
|
|
12
|
+
{ $or: [{ lockedBy: { $exists: false } }, { lockedBy: null }] },
|
|
13
|
+
],
|
|
14
|
+
attemptCount: 0,
|
|
15
|
+
status: "pending",
|
|
60
16
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return [3 /*break*/, 4];
|
|
72
|
-
case 2:
|
|
73
|
-
error_1 = _a.sent();
|
|
74
|
-
return [4 /*yield*/, removeUntouchedPendingJob(job._id)];
|
|
75
|
-
case 3:
|
|
76
|
-
compensationSucceeded = _a.sent();
|
|
77
|
-
if (!compensationSucceeded) {
|
|
78
|
-
api_1.logger.error("[jobs] Dispatch failed for job ".concat(job._id.toString(), " and compensation lost race \u2014 row was already claimed or mutated"));
|
|
79
|
-
}
|
|
80
|
-
throw new dispatchError_1.JobDispatchError({
|
|
81
|
-
cause: error_1,
|
|
82
|
-
compensationSucceeded: compensationSucceeded,
|
|
83
|
-
jobId: job._id.toString(),
|
|
84
|
-
});
|
|
85
|
-
case 4: return [2 /*return*/];
|
|
17
|
+
return removed !== null;
|
|
18
|
+
};
|
|
19
|
+
const dispatchJobToRunner = async (runner, job) => {
|
|
20
|
+
try {
|
|
21
|
+
await runner.enqueue(job);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
const compensationSucceeded = await removeUntouchedPendingJob(job._id);
|
|
25
|
+
if (!compensationSucceeded) {
|
|
26
|
+
api_1.logger.error(`[jobs] Dispatch failed for job ${job._id.toString()} and compensation lost race — row was already claimed or mutated`);
|
|
86
27
|
}
|
|
87
|
-
|
|
88
|
-
|
|
28
|
+
throw new dispatchError_1.JobDispatchError({
|
|
29
|
+
cause: error,
|
|
30
|
+
compensationSucceeded,
|
|
31
|
+
jobId: job._id.toString(),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
89
35
|
exports.dispatchJobToRunner = dispatchJobToRunner;
|
package/dist/dispatchError.js
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __extends = (this && this.__extends) || (function () {
|
|
3
|
-
var extendStatics = function (d, b) {
|
|
4
|
-
extendStatics = Object.setPrototypeOf ||
|
|
5
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
-
return extendStatics(d, b);
|
|
8
|
-
};
|
|
9
|
-
return function (d, b) {
|
|
10
|
-
if (typeof b !== "function" && b !== null)
|
|
11
|
-
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
-
extendStatics(d, b);
|
|
13
|
-
function __() { this.constructor = d; }
|
|
14
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
-
};
|
|
16
|
-
})();
|
|
17
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
3
|
exports.JobDispatchError = void 0;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
4
|
+
class JobDispatchError extends Error {
|
|
5
|
+
cause;
|
|
6
|
+
compensationSucceeded;
|
|
7
|
+
jobId;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
const detail = options.cause instanceof Error ? options.cause.message : String(options.cause);
|
|
10
|
+
super(`Job dispatch failed for ${options.jobId}: ${detail}`);
|
|
11
|
+
this.name = "JobDispatchError";
|
|
12
|
+
this.cause = options.cause;
|
|
13
|
+
this.compensationSucceeded = options.compensationSucceeded;
|
|
14
|
+
this.jobId = options.jobId;
|
|
30
15
|
}
|
|
31
|
-
|
|
32
|
-
}(Error));
|
|
16
|
+
}
|
|
33
17
|
exports.JobDispatchError = JobDispatchError;
|