@orion-js/dogs 4.6.6 → 4.6.8
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/dist/index.cjs +161 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +161 -24
- package/dist/index.js.map +1 -1
- package/dist/repos/JobsRepo.d.ts +1 -0
- package/dist/services/Executor.d.ts +6 -0
- package/dist/types/StartConfig.d.ts +18 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -524,6 +524,17 @@ var JobsRepo = class {
|
|
|
524
524
|
);
|
|
525
525
|
return result.matchedCount > 0;
|
|
526
526
|
}
|
|
527
|
+
async rescheduleAfterExecutionGuardAcquisitionFailure(jobId, lockId, nextRunAt) {
|
|
528
|
+
const result = await this.jobs.updateOne(
|
|
529
|
+
{ _id: jobId, lockId, tries: { $gte: 1 } },
|
|
530
|
+
{
|
|
531
|
+
$set: { nextRunAt },
|
|
532
|
+
$inc: { tries: -1 },
|
|
533
|
+
$unset: { lockId: "", lockedUntil: "", claimWasStale: "" }
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
return result.matchedCount > 0;
|
|
537
|
+
}
|
|
527
538
|
async deleteEventJob(jobId, lockId) {
|
|
528
539
|
const result = await this.jobs.deleteOne({
|
|
529
540
|
_id: jobId,
|
|
@@ -894,39 +905,102 @@ var Executor = class {
|
|
|
894
905
|
return job.lockTime ?? jobToRun.lockTime;
|
|
895
906
|
}
|
|
896
907
|
getContext(job, jobToRun, onStale, isExecutionActive) {
|
|
897
|
-
var _a;
|
|
898
908
|
const effectiveLockTime = this.getEffectiveLockTime(job, jobToRun);
|
|
899
|
-
let staleTimeout = setTimeout(() => onStale(), effectiveLockTime);
|
|
900
|
-
(_a = staleTimeout.unref) == null ? void 0 : _a.call(staleTimeout);
|
|
901
909
|
const contextLogger = import_logger3.logger.addMetadata({
|
|
902
910
|
jobName: jobToRun.name,
|
|
903
911
|
jobId: jobToRun.jobId
|
|
904
912
|
});
|
|
913
|
+
let staleTimeout;
|
|
914
|
+
let watchdogCleared = false;
|
|
915
|
+
let staleReported = false;
|
|
916
|
+
let pendingExtraTime;
|
|
917
|
+
let renewalRunning = false;
|
|
918
|
+
let renewalPromise;
|
|
919
|
+
const clearCurrentStaleTimeout = () => {
|
|
920
|
+
if (staleTimeout === void 0) return;
|
|
921
|
+
clearTimeout(staleTimeout);
|
|
922
|
+
staleTimeout = void 0;
|
|
923
|
+
};
|
|
924
|
+
const reportStale = () => {
|
|
925
|
+
if (staleReported) return;
|
|
926
|
+
staleReported = true;
|
|
927
|
+
watchdogCleared = true;
|
|
928
|
+
pendingExtraTime = void 0;
|
|
929
|
+
clearCurrentStaleTimeout();
|
|
930
|
+
onStale();
|
|
931
|
+
};
|
|
932
|
+
const armStaleTimeout = (lockedUntil) => {
|
|
933
|
+
var _a;
|
|
934
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) return;
|
|
935
|
+
clearCurrentStaleTimeout();
|
|
936
|
+
staleTimeout = setTimeout(
|
|
937
|
+
() => {
|
|
938
|
+
staleTimeout = void 0;
|
|
939
|
+
reportStale();
|
|
940
|
+
},
|
|
941
|
+
Math.max(0, lockedUntil - Date.now())
|
|
942
|
+
);
|
|
943
|
+
(_a = staleTimeout.unref) == null ? void 0 : _a.call(staleTimeout);
|
|
944
|
+
};
|
|
945
|
+
const runRenewals = async () => {
|
|
946
|
+
try {
|
|
947
|
+
while (pendingExtraTime !== void 0) {
|
|
948
|
+
const extraTime = pendingExtraTime;
|
|
949
|
+
pendingExtraTime = void 0;
|
|
950
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
951
|
+
clearCurrentStaleTimeout();
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
clearCurrentStaleTimeout();
|
|
955
|
+
const lockedUntil = Date.now() + extraTime;
|
|
956
|
+
let didExtend = false;
|
|
957
|
+
try {
|
|
958
|
+
didExtend = await this.jobsRepo.extendLockTime(
|
|
959
|
+
jobToRun.jobId,
|
|
960
|
+
extraTime,
|
|
961
|
+
jobToRun.lockId
|
|
962
|
+
);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
reportStale();
|
|
965
|
+
throw error;
|
|
966
|
+
}
|
|
967
|
+
if (!didExtend) {
|
|
968
|
+
reportStale();
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
972
|
+
pendingExtraTime = void 0;
|
|
973
|
+
clearCurrentStaleTimeout();
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
armStaleTimeout(lockedUntil);
|
|
977
|
+
}
|
|
978
|
+
} finally {
|
|
979
|
+
renewalRunning = false;
|
|
980
|
+
renewalPromise = void 0;
|
|
981
|
+
}
|
|
982
|
+
};
|
|
983
|
+
armStaleTimeout(Date.now() + effectiveLockTime);
|
|
905
984
|
return {
|
|
906
985
|
definition: job,
|
|
907
986
|
record: jobToRun,
|
|
908
987
|
tries: jobToRun.tries || 0,
|
|
909
|
-
clearStaleTimeout: () =>
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
988
|
+
clearStaleTimeout: () => {
|
|
989
|
+
watchdogCleared = true;
|
|
990
|
+
pendingExtraTime = void 0;
|
|
991
|
+
clearCurrentStaleTimeout();
|
|
992
|
+
},
|
|
993
|
+
extendLockTime: (extraTime) => {
|
|
994
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
913
995
|
contextLogger.warn(`Will not extend lock for stale execution "${jobToRun.executionId}"`);
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
clearTimeout(staleTimeout);
|
|
917
|
-
let didExtend = false;
|
|
918
|
-
try {
|
|
919
|
-
didExtend = await this.jobsRepo.extendLockTime(jobToRun.jobId, extraTime, jobToRun.lockId);
|
|
920
|
-
} catch (error) {
|
|
921
|
-
onStale();
|
|
922
|
-
throw error;
|
|
996
|
+
return Promise.resolve();
|
|
923
997
|
}
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
998
|
+
pendingExtraTime = Math.max(pendingExtraTime ?? extraTime, extraTime);
|
|
999
|
+
if (!renewalRunning) {
|
|
1000
|
+
renewalRunning = true;
|
|
1001
|
+
renewalPromise = runRenewals();
|
|
927
1002
|
}
|
|
928
|
-
|
|
929
|
-
(_a2 = staleTimeout.unref) == null ? void 0 : _a2.call(staleTimeout);
|
|
1003
|
+
return renewalPromise ?? Promise.resolve();
|
|
930
1004
|
},
|
|
931
1005
|
logger: contextLogger
|
|
932
1006
|
};
|
|
@@ -1061,6 +1135,42 @@ var Executor = class {
|
|
|
1061
1135
|
}
|
|
1062
1136
|
return false;
|
|
1063
1137
|
}
|
|
1138
|
+
async handleExecutionGuardAcquisitionFailure(error, config, jobToRun, context) {
|
|
1139
|
+
context.logger.error(`Error acquiring execution guard for job "${jobToRun.name}"`, { error });
|
|
1140
|
+
const nextRunAt = new Date(Date.now() + (config.executionGuardRetryDelayMs ?? 1e3));
|
|
1141
|
+
try {
|
|
1142
|
+
const didReschedule = await this.jobsRepo.rescheduleAfterExecutionGuardAcquisitionFailure(
|
|
1143
|
+
jobToRun.jobId,
|
|
1144
|
+
jobToRun.lockId,
|
|
1145
|
+
nextRunAt
|
|
1146
|
+
);
|
|
1147
|
+
if (!didReschedule) {
|
|
1148
|
+
context.logger.warn(
|
|
1149
|
+
`Could not requeue job "${jobToRun.name}" after execution guard acquisition failed because its lock is no longer owned`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
} catch (rescheduleError) {
|
|
1153
|
+
context.logger.error(
|
|
1154
|
+
`Error requeueing job "${jobToRun.name}" after execution guard acquisition failed`,
|
|
1155
|
+
{ error: rescheduleError }
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
async releaseExecutionGuardLease(lease, jobToRun, context) {
|
|
1160
|
+
try {
|
|
1161
|
+
await lease.release();
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
context.logger.error(`Error releasing execution guard for job "${jobToRun.name}"`, { error });
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
async runGuardedResolver(job, jobToRun, context, lease) {
|
|
1167
|
+
try {
|
|
1168
|
+
return await job.resolve(jobToRun.params, context);
|
|
1169
|
+
} finally {
|
|
1170
|
+
context.clearStaleTimeout();
|
|
1171
|
+
await this.releaseExecutionGuardLease(lease, jobToRun, context);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1064
1174
|
async executeJob(config, jobToRun, onExecutionStale) {
|
|
1065
1175
|
const job = this.getJobDefinition(jobToRun, config.jobs);
|
|
1066
1176
|
if (!job) return;
|
|
@@ -1142,13 +1252,36 @@ var Executor = class {
|
|
|
1142
1252
|
params: jobToRun.params
|
|
1143
1253
|
};
|
|
1144
1254
|
await (0, import_logger3.runWithOrionAsyncContext)(extraContext, async () => {
|
|
1145
|
-
|
|
1255
|
+
let executionGuardLease;
|
|
1256
|
+
if (config.executionGuard) {
|
|
1146
1257
|
(0, import_logger3.updateOrionAsyncContext)({
|
|
1147
1258
|
jobName: jobToRun.name,
|
|
1148
1259
|
jobId: jobToRun.jobId,
|
|
1149
1260
|
params: jobToRun.params
|
|
1150
1261
|
});
|
|
1151
|
-
|
|
1262
|
+
try {
|
|
1263
|
+
executionGuardLease = await config.executionGuard.acquire(jobToRun);
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
context.clearStaleTimeout();
|
|
1266
|
+
await this.handleExecutionGuardAcquisitionFailure(error, config, jobToRun, context);
|
|
1267
|
+
if (executionStatus === "stale") await staleHandlingPromise;
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
if (executionStatus === "stale") {
|
|
1271
|
+
await staleHandlingPromise;
|
|
1272
|
+
await this.releaseExecutionGuardLease(executionGuardLease, jobToRun, context);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
try {
|
|
1277
|
+
if (!config.executionGuard) {
|
|
1278
|
+
(0, import_logger3.updateOrionAsyncContext)({
|
|
1279
|
+
jobName: jobToRun.name,
|
|
1280
|
+
jobId: jobToRun.jobId,
|
|
1281
|
+
params: jobToRun.params
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
const result = executionGuardLease ? await this.runGuardedResolver(job, jobToRun, context, executionGuardLease) : await job.resolve(jobToRun.params, context);
|
|
1152
1285
|
context.clearStaleTimeout();
|
|
1153
1286
|
if (executionStatus === "stale") {
|
|
1154
1287
|
await staleHandlingPromise;
|
|
@@ -1327,7 +1460,11 @@ var WorkerService = class {
|
|
|
1327
1460
|
jobs: config.jobs,
|
|
1328
1461
|
maxTries: config.maxTries,
|
|
1329
1462
|
maxTriesReachedRetentionMs: config.maxTriesReachedRetentionMs,
|
|
1330
|
-
onMaxTriesReached: config.onMaxTriesReached
|
|
1463
|
+
onMaxTriesReached: config.onMaxTriesReached,
|
|
1464
|
+
...config.executionGuard ? {
|
|
1465
|
+
executionGuard: config.executionGuard,
|
|
1466
|
+
executionGuardRetryDelayMs: Math.max(config.pollInterval, 1e3)
|
|
1467
|
+
} : {}
|
|
1331
1468
|
};
|
|
1332
1469
|
import_logger4.logger.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
|
|
1333
1470
|
let consecutivePartitionMisses = 0;
|