@terreno/jobs 57.4.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/LICENSE +21 -0
- package/README.md +56 -0
- package/biome.jsonc +20 -0
- package/bunfig.toml +5 -0
- package/dist/activeJobExecution.d.ts +3 -0
- package/dist/activeJobExecution.js +29 -0
- package/dist/claimLock.d.ts +16 -0
- package/dist/claimLock.js +34 -0
- package/dist/dispatchCompensation.d.ts +3 -0
- package/dist/dispatchCompensation.js +89 -0
- package/dist/dispatchError.d.ts +10 -0
- package/dist/dispatchError.js +33 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +15 -0
- package/dist/jobExecutor.d.ts +34 -0
- package/dist/jobExecutor.js +328 -0
- package/dist/jobsApp.d.ts +61 -0
- package/dist/jobsApp.js +308 -0
- package/dist/jobsService.d.ts +38 -0
- package/dist/jobsService.js +505 -0
- package/dist/modelTypes.d.ts +52 -0
- package/dist/modelTypes.js +4 -0
- package/dist/models/job.d.ts +2 -0
- package/dist/models/job.js +124 -0
- package/dist/models/jobSchedule.d.ts +2 -0
- package/dist/models/jobSchedule.js +48 -0
- package/dist/retryBackoff.d.ts +17 -0
- package/dist/retryBackoff.js +22 -0
- package/dist/routes/jobsAdmin.d.ts +15 -0
- package/dist/routes/jobsAdmin.js +596 -0
- package/dist/routes/jobsExecute.d.ts +12 -0
- package/dist/routes/jobsExecute.js +119 -0
- package/dist/runners/gcpCloudTasks.d.ts +78 -0
- package/dist/runners/gcpCloudTasks.js +256 -0
- package/dist/runners/mongoRunner.d.ts +14 -0
- package/dist/runners/mongoRunner.js +204 -0
- package/dist/runners/vercelQueues.d.ts +111 -0
- package/dist/runners/vercelQueues.js +326 -0
- package/dist/scheduleCron.d.ts +15 -0
- package/dist/scheduleCron.js +61 -0
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.js +178 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.js +2 -0
- package/package.json +82 -0
- package/src/activeJobExecution.ts +28 -0
- package/src/claimLock.ts +37 -0
- package/src/dispatchCompensation.ts +39 -0
- package/src/dispatchError.ts +14 -0
- package/src/index.ts +24 -0
- package/src/jobExecutor.ts +333 -0
- package/src/jobsApp.ts +255 -0
- package/src/jobsService.ts +395 -0
- package/src/modelTypes.ts +78 -0
- package/src/models/job.ts +129 -0
- package/src/models/jobSchedule.ts +57 -0
- package/src/retryBackoff.ts +34 -0
- package/src/routes/jobsAdmin.ts +562 -0
- package/src/routes/jobsExecute.ts +95 -0
- package/src/runners/gcpCloudTasks.ts +290 -0
- package/src/runners/mongoRunner.ts +135 -0
- package/src/runners/vercelQueues.ts +394 -0
- package/src/scheduleCron.ts +65 -0
- package/src/scheduler.ts +112 -0
- package/src/tests/bunSetup.ts +9 -0
- package/src/tests/gcpCloudTasksRunner.test.ts +349 -0
- package/src/tests/jobModel.test.ts +62 -0
- package/src/tests/jobScheduleModel.test.ts +44 -0
- package/src/tests/jobsAdmin.test.ts +728 -0
- package/src/tests/jobsApp.test.ts +113 -0
- package/src/tests/jobsCustomRunner.test.ts +367 -0
- package/src/tests/jobsDelayIdempotency.test.ts +255 -0
- package/src/tests/jobsExecute.test.ts +640 -0
- package/src/tests/jobsRetry.test.ts +420 -0
- package/src/tests/jobsSchedule.test.ts +505 -0
- package/src/tests/jobsTracer.test.ts +165 -0
- package/src/tests/jobsWorkerLifecycle.test.ts +509 -0
- package/src/tests/packageExports.test.ts +24 -0
- package/src/tests/retryBackoff.test.ts +76 -0
- package/src/tests/scheduleCron.test.ts +87 -0
- package/src/tests/vercelQueuesRunner.test.ts +524 -0
- package/src/types.ts +74 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Job = void 0;
|
|
7
|
+
var api_1 = require("@terreno/api");
|
|
8
|
+
var mongoose_1 = __importDefault(require("mongoose"));
|
|
9
|
+
var jobSchema = new mongoose_1.default.Schema({
|
|
10
|
+
attemptCount: {
|
|
11
|
+
default: 0,
|
|
12
|
+
description: "Number of execution attempts recorded for this job",
|
|
13
|
+
type: Number,
|
|
14
|
+
},
|
|
15
|
+
attempts: {
|
|
16
|
+
default: [],
|
|
17
|
+
description: "History of execution attempts including errors",
|
|
18
|
+
type: [
|
|
19
|
+
{
|
|
20
|
+
_id: false,
|
|
21
|
+
at: {
|
|
22
|
+
description: "When this execution attempt ran",
|
|
23
|
+
required: true,
|
|
24
|
+
type: Date,
|
|
25
|
+
},
|
|
26
|
+
error: {
|
|
27
|
+
description: "Error message captured for this attempt",
|
|
28
|
+
type: String,
|
|
29
|
+
},
|
|
30
|
+
errorClass: {
|
|
31
|
+
description: "Classified error category for this attempt",
|
|
32
|
+
type: String,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
backoffMs: {
|
|
38
|
+
default: 1000,
|
|
39
|
+
description: "Base delay in milliseconds for exponential retry backoff",
|
|
40
|
+
type: Number,
|
|
41
|
+
},
|
|
42
|
+
idempotencyKey: {
|
|
43
|
+
description: "Optional deduplication key scoped to the job name",
|
|
44
|
+
type: String,
|
|
45
|
+
},
|
|
46
|
+
lastError: {
|
|
47
|
+
description: "Most recent error message from a failed attempt",
|
|
48
|
+
type: String,
|
|
49
|
+
},
|
|
50
|
+
lockedAt: {
|
|
51
|
+
description: "When this job row was claimed by a worker",
|
|
52
|
+
type: Date,
|
|
53
|
+
},
|
|
54
|
+
lockedBy: {
|
|
55
|
+
description: "Worker identity that claimed this job row",
|
|
56
|
+
type: String,
|
|
57
|
+
},
|
|
58
|
+
maxAttempts: {
|
|
59
|
+
default: 5,
|
|
60
|
+
description: "Maximum execution attempts before the job is dead-lettered",
|
|
61
|
+
type: Number,
|
|
62
|
+
},
|
|
63
|
+
maxBackoffMs: {
|
|
64
|
+
default: 15 * 60 * 1000,
|
|
65
|
+
description: "Maximum retry delay in milliseconds before backoff is capped",
|
|
66
|
+
type: Number,
|
|
67
|
+
},
|
|
68
|
+
name: {
|
|
69
|
+
description: "Registered handler name for this job",
|
|
70
|
+
index: true,
|
|
71
|
+
required: true,
|
|
72
|
+
type: String,
|
|
73
|
+
},
|
|
74
|
+
payload: {
|
|
75
|
+
description: "JSON-serializable input passed to the handler",
|
|
76
|
+
type: mongoose_1.default.Schema.Types.Mixed,
|
|
77
|
+
},
|
|
78
|
+
payloadRedacted: {
|
|
79
|
+
default: false,
|
|
80
|
+
description: "Whether the stored payload has been redacted for admin display",
|
|
81
|
+
type: Boolean,
|
|
82
|
+
},
|
|
83
|
+
retriedById: {
|
|
84
|
+
description: "Follow-up job created by an admin retry action",
|
|
85
|
+
ref: "Job",
|
|
86
|
+
type: mongoose_1.default.Schema.Types.ObjectId,
|
|
87
|
+
},
|
|
88
|
+
retriedFromId: {
|
|
89
|
+
description: "Original job row that this retry was created from",
|
|
90
|
+
ref: "Job",
|
|
91
|
+
type: mongoose_1.default.Schema.Types.ObjectId,
|
|
92
|
+
},
|
|
93
|
+
runAt: {
|
|
94
|
+
description: "Earliest time a worker may claim this job",
|
|
95
|
+
index: true,
|
|
96
|
+
required: true,
|
|
97
|
+
type: Date,
|
|
98
|
+
},
|
|
99
|
+
scheduleId: {
|
|
100
|
+
description: "Recurring schedule that enqueued this job, when applicable",
|
|
101
|
+
ref: "JobSchedule",
|
|
102
|
+
type: mongoose_1.default.Schema.Types.ObjectId,
|
|
103
|
+
},
|
|
104
|
+
status: {
|
|
105
|
+
description: "Current lifecycle status of the job row",
|
|
106
|
+
enum: ["cancelled", "completed", "dead", "failed", "pending", "running", "scheduled"],
|
|
107
|
+
index: true,
|
|
108
|
+
required: true,
|
|
109
|
+
type: String,
|
|
110
|
+
},
|
|
111
|
+
}, { strict: "throw", toJSON: { virtuals: true }, toObject: { virtuals: true } });
|
|
112
|
+
jobSchema.plugin(api_1.createdUpdatedPlugin);
|
|
113
|
+
jobSchema.plugin(api_1.findOneOrNone);
|
|
114
|
+
jobSchema.plugin(api_1.findExactlyOne);
|
|
115
|
+
jobSchema.index(
|
|
116
|
+
// biome-ignore assist/source/useSortedKeys: IP specifies (name, idempotencyKey) index field order
|
|
117
|
+
{ name: 1, idempotencyKey: 1 }, {
|
|
118
|
+
partialFilterExpression: { idempotencyKey: { $exists: true, $type: "string" } },
|
|
119
|
+
unique: true,
|
|
120
|
+
});
|
|
121
|
+
// biome-ignore assist/source/useSortedKeys: IP specifies (status, runAt) claim index field order
|
|
122
|
+
jobSchema.index({ status: 1, runAt: 1 });
|
|
123
|
+
jobSchema.index({ scheduleId: 1, status: 1 });
|
|
124
|
+
exports.Job = mongoose_1.default.model("Job", jobSchema);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JobSchedule = void 0;
|
|
7
|
+
var api_1 = require("@terreno/api");
|
|
8
|
+
var mongoose_1 = __importDefault(require("mongoose"));
|
|
9
|
+
var jobScheduleSchema = new mongoose_1.default.Schema({
|
|
10
|
+
cron: {
|
|
11
|
+
description: "Cron expression that controls when this schedule fires",
|
|
12
|
+
required: true,
|
|
13
|
+
type: String,
|
|
14
|
+
},
|
|
15
|
+
enabled: {
|
|
16
|
+
default: true,
|
|
17
|
+
description: "Whether the worker should enqueue jobs for this schedule",
|
|
18
|
+
type: Boolean,
|
|
19
|
+
},
|
|
20
|
+
handlerName: {
|
|
21
|
+
description: "Registered job handler name invoked for each schedule tick",
|
|
22
|
+
required: true,
|
|
23
|
+
type: String,
|
|
24
|
+
},
|
|
25
|
+
name: {
|
|
26
|
+
description: "Unique schedule name matching the registered job definition",
|
|
27
|
+
required: true,
|
|
28
|
+
type: String,
|
|
29
|
+
unique: true,
|
|
30
|
+
},
|
|
31
|
+
nextRunAt: {
|
|
32
|
+
description: "Next UTC instant when the scheduler should evaluate this schedule",
|
|
33
|
+
index: true,
|
|
34
|
+
required: true,
|
|
35
|
+
type: Date,
|
|
36
|
+
},
|
|
37
|
+
timezone: {
|
|
38
|
+
default: "UTC",
|
|
39
|
+
description: "IANA timezone used to interpret the cron expression",
|
|
40
|
+
required: true,
|
|
41
|
+
type: String,
|
|
42
|
+
},
|
|
43
|
+
}, { strict: "throw", toJSON: { virtuals: true }, toObject: { virtuals: true } });
|
|
44
|
+
jobScheduleSchema.plugin(api_1.createdUpdatedPlugin);
|
|
45
|
+
jobScheduleSchema.plugin(api_1.findOneOrNone);
|
|
46
|
+
jobScheduleSchema.plugin(api_1.findExactlyOne);
|
|
47
|
+
jobScheduleSchema.index({ enabled: 1, nextRunAt: 1 });
|
|
48
|
+
exports.JobSchedule = mongoose_1.default.model("JobSchedule", jobScheduleSchema);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DateTime } from "luxon";
|
|
2
|
+
export declare const DEFAULT_BACKOFF_MS = 1000;
|
|
3
|
+
export declare const DEFAULT_MAX_BACKOFF_MS: number;
|
|
4
|
+
export interface RetryBackoffOptions {
|
|
5
|
+
backoffMs: number;
|
|
6
|
+
maxBackoffMs: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ComputeRetryDelayMsParams extends RetryBackoffOptions {
|
|
9
|
+
attemptCount: number;
|
|
10
|
+
random?: () => number;
|
|
11
|
+
}
|
|
12
|
+
export interface ComputeRetryRunAtParams extends ComputeRetryDelayMsParams {
|
|
13
|
+
from: DateTime;
|
|
14
|
+
}
|
|
15
|
+
/** @internal */
|
|
16
|
+
export declare const computeRetryDelayMs: (params: ComputeRetryDelayMsParams) => number;
|
|
17
|
+
export declare const computeRetryRunAt: (params: ComputeRetryRunAtParams) => DateTime;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeRetryRunAt = exports.computeRetryDelayMs = exports.DEFAULT_MAX_BACKOFF_MS = exports.DEFAULT_BACKOFF_MS = void 0;
|
|
4
|
+
exports.DEFAULT_BACKOFF_MS = 1000;
|
|
5
|
+
exports.DEFAULT_MAX_BACKOFF_MS = 15 * 60 * 1000;
|
|
6
|
+
var equalJitterFactor = function (random) { return 0.5 + random() * 0.5; };
|
|
7
|
+
/** @internal */
|
|
8
|
+
var computeRetryDelayMs = function (params) {
|
|
9
|
+
var _a;
|
|
10
|
+
var random = (_a = params.random) !== null && _a !== void 0 ? _a : Math.random;
|
|
11
|
+
var exponent = Math.max(0, params.attemptCount - 1);
|
|
12
|
+
var baseDelayMs = params.backoffMs * Math.pow(2, exponent);
|
|
13
|
+
var cappedDelayMs = Math.min(params.maxBackoffMs, baseDelayMs);
|
|
14
|
+
return Math.round(cappedDelayMs * equalJitterFactor(random));
|
|
15
|
+
};
|
|
16
|
+
exports.computeRetryDelayMs = computeRetryDelayMs;
|
|
17
|
+
var computeRetryRunAt = function (params) {
|
|
18
|
+
return params.from.plus({
|
|
19
|
+
milliseconds: (0, exports.computeRetryDelayMs)(params),
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
exports.computeRetryRunAt = computeRetryRunAt;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AnyTerrenoAccess } from "@terreno/api";
|
|
2
|
+
import type { Application } from "express";
|
|
3
|
+
import type { JobsService } from "../jobsService";
|
|
4
|
+
import type { JobsAdminRedactPayload } from "../types";
|
|
5
|
+
export interface RegisterJobsAdminRouteOptions {
|
|
6
|
+
accessControl?: AnyTerrenoAccess;
|
|
7
|
+
basePath: string;
|
|
8
|
+
jobsService: JobsService;
|
|
9
|
+
openApi?: unknown;
|
|
10
|
+
redactPayload?: JobsAdminRedactPayload;
|
|
11
|
+
}
|
|
12
|
+
export declare const registerJobsAdminRoutes: ({ app, options, }: {
|
|
13
|
+
app: Application;
|
|
14
|
+
options: RegisterJobsAdminRouteOptions;
|
|
15
|
+
}) => void;
|