@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
|
@@ -1,119 +1,59 @@
|
|
|
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
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
3
|
exports.registerJobsExecuteRoute = void 0;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
4
|
+
const api_1 = require("@terreno/api");
|
|
5
|
+
const jobExecutor_1 = require("../jobExecutor");
|
|
6
|
+
const JOB_NOT_FOUND_TITLE = "Job not found";
|
|
7
|
+
const readJobId = (body) => {
|
|
55
8
|
if (!body || typeof body !== "object" || !("jobId" in body)) {
|
|
56
9
|
return undefined;
|
|
57
10
|
}
|
|
58
|
-
|
|
11
|
+
const { jobId } = body;
|
|
59
12
|
if (typeof jobId !== "string") {
|
|
60
13
|
return undefined;
|
|
61
14
|
}
|
|
62
|
-
|
|
15
|
+
const trimmed = jobId.trim();
|
|
63
16
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
64
17
|
};
|
|
65
|
-
|
|
66
|
-
|
|
18
|
+
const serializeJob = (job) => {
|
|
19
|
+
const json = job.toJSON();
|
|
67
20
|
if (json._id && typeof json._id === "object" && "toString" in json._id) {
|
|
68
|
-
return
|
|
21
|
+
return {
|
|
22
|
+
...json,
|
|
23
|
+
id: json._id.toString(),
|
|
24
|
+
};
|
|
69
25
|
}
|
|
70
26
|
return json;
|
|
71
27
|
};
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
case 4:
|
|
94
|
-
if (!authorized) {
|
|
95
|
-
throw new api_1.UnauthorizedError("Unauthorized");
|
|
96
|
-
}
|
|
97
|
-
jobId = readJobId(req.body);
|
|
98
|
-
if (!jobId) {
|
|
99
|
-
throw new api_1.APIError({ status: 400, title: "jobId is required" });
|
|
100
|
-
}
|
|
101
|
-
return [4 /*yield*/, (0, jobExecutor_1.executeJobById)({
|
|
102
|
-
host: options.host,
|
|
103
|
-
jobId: jobId,
|
|
104
|
-
signal: AbortSignal.timeout(30 * 60 * 1000),
|
|
105
|
-
})];
|
|
106
|
-
case 5:
|
|
107
|
-
outcome = _c.sent();
|
|
108
|
-
if (outcome.kind === "not_found") {
|
|
109
|
-
throw new api_1.NotFoundError(JOB_NOT_FOUND_TITLE);
|
|
110
|
-
}
|
|
111
|
-
if (outcome.kind === "conflict") {
|
|
112
|
-
throw new api_1.ConflictError(outcome.reason === "not_due" ? "Job is not due yet" : "Job is already running");
|
|
113
|
-
}
|
|
114
|
-
return [2 /*return*/, res.status(200).json({ data: serializeJob(outcome.job) })];
|
|
115
|
-
}
|
|
28
|
+
const registerJobsExecuteRoute = ({ app, options, }) => {
|
|
29
|
+
const path = `${options.basePath}/execute`;
|
|
30
|
+
app.post(path, (0, api_1.asyncHandler)(async (req, res) => {
|
|
31
|
+
let authorized = false;
|
|
32
|
+
try {
|
|
33
|
+
authorized = Boolean(await options.executeAuth(req));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
authorized = false;
|
|
37
|
+
}
|
|
38
|
+
if (!authorized) {
|
|
39
|
+
throw new api_1.UnauthorizedError("Unauthorized");
|
|
40
|
+
}
|
|
41
|
+
const jobId = readJobId(req.body);
|
|
42
|
+
if (!jobId) {
|
|
43
|
+
throw new api_1.APIError({ status: 400, title: "jobId is required" });
|
|
44
|
+
}
|
|
45
|
+
const outcome = await (0, jobExecutor_1.executeJobById)({
|
|
46
|
+
host: options.host,
|
|
47
|
+
jobId,
|
|
48
|
+
signal: AbortSignal.timeout(30 * 60 * 1_000),
|
|
116
49
|
});
|
|
117
|
-
|
|
50
|
+
if (outcome.kind === "not_found") {
|
|
51
|
+
throw new api_1.NotFoundError(JOB_NOT_FOUND_TITLE);
|
|
52
|
+
}
|
|
53
|
+
if (outcome.kind === "conflict") {
|
|
54
|
+
throw new api_1.ConflictError(outcome.reason === "not_due" ? "Job is not due yet" : "Job is already running");
|
|
55
|
+
}
|
|
56
|
+
return res.status(200).json({ data: serializeJob(outcome.job) });
|
|
57
|
+
}));
|
|
118
58
|
};
|
|
119
59
|
exports.registerJobsExecuteRoute = registerJobsExecuteRoute;
|
|
@@ -1,145 +1,102 @@
|
|
|
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.GcpCloudTasksRunner = exports.GCP_HTTP_TASK_DEFAULT_DISPATCH_DEADLINE_SECONDS = exports.GCP_HTTP_TASK_MAX_DISPATCH_DEADLINE_SECONDS = exports.GCP_HTTP_TASK_MIN_DISPATCH_DEADLINE_SECONDS = void 0;
|
|
40
|
-
|
|
41
|
-
|
|
4
|
+
const node_module_1 = require("node:module");
|
|
5
|
+
const luxon_1 = require("luxon");
|
|
42
6
|
/** Cloud Tasks HTTP `dispatchDeadline` bounds (15s–30min). Default 30min matches Cloud Run. */
|
|
43
7
|
exports.GCP_HTTP_TASK_MIN_DISPATCH_DEADLINE_SECONDS = 15;
|
|
44
|
-
exports.GCP_HTTP_TASK_MAX_DISPATCH_DEADLINE_SECONDS =
|
|
45
|
-
exports.GCP_HTTP_TASK_DEFAULT_DISPATCH_DEADLINE_SECONDS =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
8
|
+
exports.GCP_HTTP_TASK_MAX_DISPATCH_DEADLINE_SECONDS = 1_800;
|
|
9
|
+
exports.GCP_HTTP_TASK_DEFAULT_DISPATCH_DEADLINE_SECONDS = 1_800;
|
|
10
|
+
const nodeRequire = (0, node_module_1.createRequire)(__filename);
|
|
11
|
+
const requireNonEmpty = (value, label) => {
|
|
12
|
+
const trimmed = value?.trim();
|
|
49
13
|
if (!trimmed) {
|
|
50
|
-
throw new Error(
|
|
14
|
+
throw new Error(`GcpCloudTasksRunner requires ${label}`);
|
|
51
15
|
}
|
|
52
16
|
return trimmed;
|
|
53
17
|
};
|
|
54
|
-
|
|
55
|
-
|
|
18
|
+
const normalizePublicUrl = (publicUrl) => {
|
|
19
|
+
let parsed;
|
|
56
20
|
try {
|
|
57
21
|
parsed = new URL(publicUrl);
|
|
58
22
|
}
|
|
59
|
-
catch
|
|
60
|
-
throw new Error(
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`GcpCloudTasksRunner publicUrl must be an absolute http(s) URL, received: ${publicUrl}`);
|
|
61
25
|
}
|
|
62
26
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
63
|
-
throw new Error(
|
|
27
|
+
throw new Error(`GcpCloudTasksRunner publicUrl must use http or https, received: ${publicUrl}`);
|
|
64
28
|
}
|
|
65
29
|
return parsed.toString().replace(/\/+$/, "");
|
|
66
30
|
};
|
|
67
|
-
|
|
68
|
-
|
|
31
|
+
const normalizeBasePath = (basePath) => {
|
|
32
|
+
const trimmed = (basePath ?? "/jobs").trim();
|
|
69
33
|
if (!trimmed.startsWith("/")) {
|
|
70
|
-
throw new Error(
|
|
34
|
+
throw new Error(`GcpCloudTasksRunner basePath must start with "/", received: ${basePath}`);
|
|
71
35
|
}
|
|
72
36
|
return trimmed.replace(/\/+$/, "") || "/";
|
|
73
37
|
};
|
|
74
|
-
|
|
38
|
+
const validateDispatchDeadlineSeconds = (dispatchDeadlineSeconds) => {
|
|
75
39
|
if (dispatchDeadlineSeconds === undefined) {
|
|
76
40
|
return exports.GCP_HTTP_TASK_DEFAULT_DISPATCH_DEADLINE_SECONDS;
|
|
77
41
|
}
|
|
78
42
|
if (!Number.isFinite(dispatchDeadlineSeconds) ||
|
|
79
43
|
dispatchDeadlineSeconds < exports.GCP_HTTP_TASK_MIN_DISPATCH_DEADLINE_SECONDS ||
|
|
80
44
|
dispatchDeadlineSeconds > exports.GCP_HTTP_TASK_MAX_DISPATCH_DEADLINE_SECONDS) {
|
|
81
|
-
throw new Error(
|
|
45
|
+
throw new Error(`GcpCloudTasksRunner dispatchDeadlineSeconds must be between ${exports.GCP_HTTP_TASK_MIN_DISPATCH_DEADLINE_SECONDS} and ${exports.GCP_HTTP_TASK_MAX_DISPATCH_DEADLINE_SECONDS} seconds`);
|
|
82
46
|
}
|
|
83
47
|
return dispatchDeadlineSeconds;
|
|
84
48
|
};
|
|
85
|
-
|
|
49
|
+
const toProtobufDuration = (dispatchDeadlineSeconds) => ({
|
|
86
50
|
seconds: dispatchDeadlineSeconds,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
var loadCloudTasksClient = function () {
|
|
51
|
+
});
|
|
52
|
+
const resolveConfig = (config) => ({
|
|
53
|
+
basePath: normalizeBasePath(config.basePath),
|
|
54
|
+
dispatchDeadline: toProtobufDuration(validateDispatchDeadlineSeconds(config.dispatchDeadlineSeconds)),
|
|
55
|
+
location: requireNonEmpty(config.location, "location"),
|
|
56
|
+
oidcAudience: config.oidcAudience?.trim() || undefined,
|
|
57
|
+
project: requireNonEmpty(config.project, "project"),
|
|
58
|
+
publicUrl: normalizePublicUrl(requireNonEmpty(config.publicUrl, "publicUrl")),
|
|
59
|
+
queue: requireNonEmpty(config.queue, "queue"),
|
|
60
|
+
serviceAccountEmail: requireNonEmpty(config.serviceAccountEmail, "serviceAccountEmail"),
|
|
61
|
+
});
|
|
62
|
+
const loadCloudTasksClient = () => {
|
|
102
63
|
try {
|
|
103
|
-
|
|
104
|
-
return
|
|
64
|
+
const module = nodeRequire("@google-cloud/tasks");
|
|
65
|
+
return module.CloudTasksClient;
|
|
105
66
|
}
|
|
106
|
-
catch
|
|
67
|
+
catch {
|
|
107
68
|
throw new Error("GcpCloudTasksRunner requires optional peer dependency @google-cloud/tasks. " +
|
|
108
69
|
"Install it with: bun add @google-cloud/tasks");
|
|
109
70
|
}
|
|
110
71
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
var scheduled = luxon_1.DateTime.fromJSDate(runAt, { zone: "utc" });
|
|
116
|
-
var now = luxon_1.DateTime.utc();
|
|
72
|
+
const buildExecuteUrl = (publicUrl, basePath) => `${publicUrl}${basePath}/execute`;
|
|
73
|
+
const buildScheduleTime = (runAt) => {
|
|
74
|
+
const scheduled = luxon_1.DateTime.fromJSDate(runAt, { zone: "utc" });
|
|
75
|
+
const now = luxon_1.DateTime.utc();
|
|
117
76
|
if (scheduled <= now) {
|
|
118
77
|
return undefined;
|
|
119
78
|
}
|
|
120
79
|
return {
|
|
121
|
-
nanos: (scheduled.toMillis() % 1000) *
|
|
80
|
+
nanos: (scheduled.toMillis() % 1000) * 1_000_000,
|
|
122
81
|
seconds: Math.floor(scheduled.toSeconds()),
|
|
123
82
|
};
|
|
124
83
|
};
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
};
|
|
142
|
-
var DEFAULT_SCHEDULE_POLL_INTERVAL_MS = 1000;
|
|
84
|
+
const waitForAbortOrTimeout = (signal, timeoutMs) => new Promise((resolve) => {
|
|
85
|
+
if (signal.aborted) {
|
|
86
|
+
resolve();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const timer = setTimeout(() => {
|
|
90
|
+
signal.removeEventListener("abort", onAbort);
|
|
91
|
+
resolve();
|
|
92
|
+
}, timeoutMs);
|
|
93
|
+
const onAbort = () => {
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
resolve();
|
|
96
|
+
};
|
|
97
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
98
|
+
});
|
|
99
|
+
const DEFAULT_SCHEDULE_POLL_INTERVAL_MS = 1_000;
|
|
143
100
|
/**
|
|
144
101
|
* Dispatch-only runner that enqueues Cloud Tasks HTTP POST callbacks to
|
|
145
102
|
* `{publicUrl}{basePath}/execute` with an OIDC token.
|
|
@@ -147,110 +104,69 @@ var DEFAULT_SCHEDULE_POLL_INTERVAL_MS = 1000;
|
|
|
147
104
|
* `start()` ticks Mongo schedules so cron rows enqueue Cloud Tasks. It does not
|
|
148
105
|
* claim job rows — execution happens on POST /jobs/execute.
|
|
149
106
|
*/
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
107
|
+
class GcpCloudTasksRunner {
|
|
108
|
+
id = "gcp-cloud-tasks";
|
|
109
|
+
requiresExecuteRoute = true;
|
|
110
|
+
client;
|
|
111
|
+
config;
|
|
112
|
+
pollIntervalMs = DEFAULT_SCHEDULE_POLL_INTERVAL_MS;
|
|
113
|
+
running = false;
|
|
114
|
+
stopResolve;
|
|
115
|
+
constructor(config) {
|
|
157
116
|
this.config = resolveConfig(config);
|
|
158
|
-
this.client =
|
|
117
|
+
this.client = config.client ?? new (loadCloudTasksClient())();
|
|
159
118
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
};
|
|
197
|
-
GcpCloudTasksRunner.prototype.start = function (options) {
|
|
198
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
199
|
-
var _a, _b;
|
|
200
|
-
return __generator(this, function (_c) {
|
|
201
|
-
switch (_c.label) {
|
|
202
|
-
case 0:
|
|
203
|
-
this.pollIntervalMs = (_a = options.pollIntervalMs) !== null && _a !== void 0 ? _a : DEFAULT_SCHEDULE_POLL_INTERVAL_MS;
|
|
204
|
-
this.running = true;
|
|
205
|
-
_c.label = 1;
|
|
206
|
-
case 1:
|
|
207
|
-
_c.trys.push([1, , 7, 8]);
|
|
208
|
-
_c.label = 2;
|
|
209
|
-
case 2:
|
|
210
|
-
if (!!options.signal.aborted) return [3 /*break*/, 6];
|
|
211
|
-
return [4 /*yield*/, options.jobs.reconcileSchedulesIfDirty()];
|
|
212
|
-
case 3:
|
|
213
|
-
_c.sent();
|
|
214
|
-
return [4 /*yield*/, options.jobs.tickSchedules()];
|
|
215
|
-
case 4:
|
|
216
|
-
_c.sent();
|
|
217
|
-
if (options.signal.aborted) {
|
|
218
|
-
return [3 /*break*/, 6];
|
|
219
|
-
}
|
|
220
|
-
return [4 /*yield*/, waitForAbortOrTimeout(options.signal, this.pollIntervalMs)];
|
|
221
|
-
case 5:
|
|
222
|
-
_c.sent();
|
|
223
|
-
return [3 /*break*/, 2];
|
|
224
|
-
case 6: return [3 /*break*/, 8];
|
|
225
|
-
case 7:
|
|
226
|
-
this.running = false;
|
|
227
|
-
(_b = this.stopResolve) === null || _b === void 0 ? void 0 : _b.call(this);
|
|
228
|
-
this.stopResolve = undefined;
|
|
229
|
-
return [7 /*endfinally*/];
|
|
230
|
-
case 8: return [2 /*return*/];
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
};
|
|
235
|
-
GcpCloudTasksRunner.prototype.stop = function () {
|
|
236
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
237
|
-
var _this = this;
|
|
238
|
-
return __generator(this, function (_a) {
|
|
239
|
-
switch (_a.label) {
|
|
240
|
-
case 0:
|
|
241
|
-
if (!this.running) {
|
|
242
|
-
return [2 /*return*/];
|
|
243
|
-
}
|
|
244
|
-
return [4 /*yield*/, new Promise(function (resolve) {
|
|
245
|
-
_this.stopResolve = resolve;
|
|
246
|
-
})];
|
|
247
|
-
case 1:
|
|
248
|
-
_a.sent();
|
|
249
|
-
return [2 /*return*/];
|
|
119
|
+
async enqueue(job) {
|
|
120
|
+
const executeUrl = buildExecuteUrl(this.config.publicUrl, this.config.basePath);
|
|
121
|
+
const body = Buffer.from(JSON.stringify({ jobId: job._id.toString() })).toString("base64");
|
|
122
|
+
const task = {
|
|
123
|
+
dispatchDeadline: this.config.dispatchDeadline,
|
|
124
|
+
httpRequest: {
|
|
125
|
+
body,
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
},
|
|
129
|
+
httpMethod: "POST",
|
|
130
|
+
oidcToken: {
|
|
131
|
+
audience: this.config.oidcAudience ?? executeUrl,
|
|
132
|
+
serviceAccountEmail: this.config.serviceAccountEmail,
|
|
133
|
+
},
|
|
134
|
+
url: executeUrl,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const scheduleTime = buildScheduleTime(job.runAt);
|
|
138
|
+
if (scheduleTime) {
|
|
139
|
+
task.scheduleTime = scheduleTime;
|
|
140
|
+
}
|
|
141
|
+
const parent = this.client.queuePath(this.config.project, this.config.location, this.config.queue);
|
|
142
|
+
await this.client.createTask({ parent, task });
|
|
143
|
+
}
|
|
144
|
+
async start(options) {
|
|
145
|
+
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_SCHEDULE_POLL_INTERVAL_MS;
|
|
146
|
+
this.running = true;
|
|
147
|
+
try {
|
|
148
|
+
while (!options.signal.aborted) {
|
|
149
|
+
await options.jobs.reconcileSchedulesIfDirty();
|
|
150
|
+
await options.jobs.tickSchedules();
|
|
151
|
+
if (options.signal.aborted) {
|
|
152
|
+
break;
|
|
250
153
|
}
|
|
251
|
-
|
|
154
|
+
await waitForAbortOrTimeout(options.signal, this.pollIntervalMs);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
this.running = false;
|
|
159
|
+
this.stopResolve?.();
|
|
160
|
+
this.stopResolve = undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async stop() {
|
|
164
|
+
if (!this.running) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
await new Promise((resolve) => {
|
|
168
|
+
this.stopResolve = resolve;
|
|
252
169
|
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
}());
|
|
170
|
+
}
|
|
171
|
+
}
|
|
256
172
|
exports.GcpCloudTasksRunner = GcpCloudTasksRunner;
|