@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.js
CHANGED
|
@@ -480,6 +480,17 @@ var JobsRepo = class {
|
|
|
480
480
|
);
|
|
481
481
|
return result.matchedCount > 0;
|
|
482
482
|
}
|
|
483
|
+
async rescheduleAfterExecutionGuardAcquisitionFailure(jobId, lockId, nextRunAt) {
|
|
484
|
+
const result = await this.jobs.updateOne(
|
|
485
|
+
{ _id: jobId, lockId, tries: { $gte: 1 } },
|
|
486
|
+
{
|
|
487
|
+
$set: { nextRunAt },
|
|
488
|
+
$inc: { tries: -1 },
|
|
489
|
+
$unset: { lockId: "", lockedUntil: "", claimWasStale: "" }
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
return result.matchedCount > 0;
|
|
493
|
+
}
|
|
483
494
|
async deleteEventJob(jobId, lockId) {
|
|
484
495
|
const result = await this.jobs.deleteOne({
|
|
485
496
|
_id: jobId,
|
|
@@ -850,39 +861,102 @@ var Executor = class {
|
|
|
850
861
|
return job.lockTime ?? jobToRun.lockTime;
|
|
851
862
|
}
|
|
852
863
|
getContext(job, jobToRun, onStale, isExecutionActive) {
|
|
853
|
-
var _a;
|
|
854
864
|
const effectiveLockTime = this.getEffectiveLockTime(job, jobToRun);
|
|
855
|
-
let staleTimeout = setTimeout(() => onStale(), effectiveLockTime);
|
|
856
|
-
(_a = staleTimeout.unref) == null ? void 0 : _a.call(staleTimeout);
|
|
857
865
|
const contextLogger = logger3.addMetadata({
|
|
858
866
|
jobName: jobToRun.name,
|
|
859
867
|
jobId: jobToRun.jobId
|
|
860
868
|
});
|
|
869
|
+
let staleTimeout;
|
|
870
|
+
let watchdogCleared = false;
|
|
871
|
+
let staleReported = false;
|
|
872
|
+
let pendingExtraTime;
|
|
873
|
+
let renewalRunning = false;
|
|
874
|
+
let renewalPromise;
|
|
875
|
+
const clearCurrentStaleTimeout = () => {
|
|
876
|
+
if (staleTimeout === void 0) return;
|
|
877
|
+
clearTimeout(staleTimeout);
|
|
878
|
+
staleTimeout = void 0;
|
|
879
|
+
};
|
|
880
|
+
const reportStale = () => {
|
|
881
|
+
if (staleReported) return;
|
|
882
|
+
staleReported = true;
|
|
883
|
+
watchdogCleared = true;
|
|
884
|
+
pendingExtraTime = void 0;
|
|
885
|
+
clearCurrentStaleTimeout();
|
|
886
|
+
onStale();
|
|
887
|
+
};
|
|
888
|
+
const armStaleTimeout = (lockedUntil) => {
|
|
889
|
+
var _a;
|
|
890
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) return;
|
|
891
|
+
clearCurrentStaleTimeout();
|
|
892
|
+
staleTimeout = setTimeout(
|
|
893
|
+
() => {
|
|
894
|
+
staleTimeout = void 0;
|
|
895
|
+
reportStale();
|
|
896
|
+
},
|
|
897
|
+
Math.max(0, lockedUntil - Date.now())
|
|
898
|
+
);
|
|
899
|
+
(_a = staleTimeout.unref) == null ? void 0 : _a.call(staleTimeout);
|
|
900
|
+
};
|
|
901
|
+
const runRenewals = async () => {
|
|
902
|
+
try {
|
|
903
|
+
while (pendingExtraTime !== void 0) {
|
|
904
|
+
const extraTime = pendingExtraTime;
|
|
905
|
+
pendingExtraTime = void 0;
|
|
906
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
907
|
+
clearCurrentStaleTimeout();
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
clearCurrentStaleTimeout();
|
|
911
|
+
const lockedUntil = Date.now() + extraTime;
|
|
912
|
+
let didExtend = false;
|
|
913
|
+
try {
|
|
914
|
+
didExtend = await this.jobsRepo.extendLockTime(
|
|
915
|
+
jobToRun.jobId,
|
|
916
|
+
extraTime,
|
|
917
|
+
jobToRun.lockId
|
|
918
|
+
);
|
|
919
|
+
} catch (error) {
|
|
920
|
+
reportStale();
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
923
|
+
if (!didExtend) {
|
|
924
|
+
reportStale();
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
928
|
+
pendingExtraTime = void 0;
|
|
929
|
+
clearCurrentStaleTimeout();
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
armStaleTimeout(lockedUntil);
|
|
933
|
+
}
|
|
934
|
+
} finally {
|
|
935
|
+
renewalRunning = false;
|
|
936
|
+
renewalPromise = void 0;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
armStaleTimeout(Date.now() + effectiveLockTime);
|
|
861
940
|
return {
|
|
862
941
|
definition: job,
|
|
863
942
|
record: jobToRun,
|
|
864
943
|
tries: jobToRun.tries || 0,
|
|
865
|
-
clearStaleTimeout: () =>
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
944
|
+
clearStaleTimeout: () => {
|
|
945
|
+
watchdogCleared = true;
|
|
946
|
+
pendingExtraTime = void 0;
|
|
947
|
+
clearCurrentStaleTimeout();
|
|
948
|
+
},
|
|
949
|
+
extendLockTime: (extraTime) => {
|
|
950
|
+
if (watchdogCleared || staleReported || !isExecutionActive()) {
|
|
869
951
|
contextLogger.warn(`Will not extend lock for stale execution "${jobToRun.executionId}"`);
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
clearTimeout(staleTimeout);
|
|
873
|
-
let didExtend = false;
|
|
874
|
-
try {
|
|
875
|
-
didExtend = await this.jobsRepo.extendLockTime(jobToRun.jobId, extraTime, jobToRun.lockId);
|
|
876
|
-
} catch (error) {
|
|
877
|
-
onStale();
|
|
878
|
-
throw error;
|
|
952
|
+
return Promise.resolve();
|
|
879
953
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
954
|
+
pendingExtraTime = Math.max(pendingExtraTime ?? extraTime, extraTime);
|
|
955
|
+
if (!renewalRunning) {
|
|
956
|
+
renewalRunning = true;
|
|
957
|
+
renewalPromise = runRenewals();
|
|
883
958
|
}
|
|
884
|
-
|
|
885
|
-
(_a2 = staleTimeout.unref) == null ? void 0 : _a2.call(staleTimeout);
|
|
959
|
+
return renewalPromise ?? Promise.resolve();
|
|
886
960
|
},
|
|
887
961
|
logger: contextLogger
|
|
888
962
|
};
|
|
@@ -1017,6 +1091,42 @@ var Executor = class {
|
|
|
1017
1091
|
}
|
|
1018
1092
|
return false;
|
|
1019
1093
|
}
|
|
1094
|
+
async handleExecutionGuardAcquisitionFailure(error, config, jobToRun, context) {
|
|
1095
|
+
context.logger.error(`Error acquiring execution guard for job "${jobToRun.name}"`, { error });
|
|
1096
|
+
const nextRunAt = new Date(Date.now() + (config.executionGuardRetryDelayMs ?? 1e3));
|
|
1097
|
+
try {
|
|
1098
|
+
const didReschedule = await this.jobsRepo.rescheduleAfterExecutionGuardAcquisitionFailure(
|
|
1099
|
+
jobToRun.jobId,
|
|
1100
|
+
jobToRun.lockId,
|
|
1101
|
+
nextRunAt
|
|
1102
|
+
);
|
|
1103
|
+
if (!didReschedule) {
|
|
1104
|
+
context.logger.warn(
|
|
1105
|
+
`Could not requeue job "${jobToRun.name}" after execution guard acquisition failed because its lock is no longer owned`
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
} catch (rescheduleError) {
|
|
1109
|
+
context.logger.error(
|
|
1110
|
+
`Error requeueing job "${jobToRun.name}" after execution guard acquisition failed`,
|
|
1111
|
+
{ error: rescheduleError }
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
async releaseExecutionGuardLease(lease, jobToRun, context) {
|
|
1116
|
+
try {
|
|
1117
|
+
await lease.release();
|
|
1118
|
+
} catch (error) {
|
|
1119
|
+
context.logger.error(`Error releasing execution guard for job "${jobToRun.name}"`, { error });
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
async runGuardedResolver(job, jobToRun, context, lease) {
|
|
1123
|
+
try {
|
|
1124
|
+
return await job.resolve(jobToRun.params, context);
|
|
1125
|
+
} finally {
|
|
1126
|
+
context.clearStaleTimeout();
|
|
1127
|
+
await this.releaseExecutionGuardLease(lease, jobToRun, context);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1020
1130
|
async executeJob(config, jobToRun, onExecutionStale) {
|
|
1021
1131
|
const job = this.getJobDefinition(jobToRun, config.jobs);
|
|
1022
1132
|
if (!job) return;
|
|
@@ -1098,13 +1208,36 @@ var Executor = class {
|
|
|
1098
1208
|
params: jobToRun.params
|
|
1099
1209
|
};
|
|
1100
1210
|
await runWithOrionAsyncContext(extraContext, async () => {
|
|
1101
|
-
|
|
1211
|
+
let executionGuardLease;
|
|
1212
|
+
if (config.executionGuard) {
|
|
1102
1213
|
updateOrionAsyncContext({
|
|
1103
1214
|
jobName: jobToRun.name,
|
|
1104
1215
|
jobId: jobToRun.jobId,
|
|
1105
1216
|
params: jobToRun.params
|
|
1106
1217
|
});
|
|
1107
|
-
|
|
1218
|
+
try {
|
|
1219
|
+
executionGuardLease = await config.executionGuard.acquire(jobToRun);
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
context.clearStaleTimeout();
|
|
1222
|
+
await this.handleExecutionGuardAcquisitionFailure(error, config, jobToRun, context);
|
|
1223
|
+
if (executionStatus === "stale") await staleHandlingPromise;
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (executionStatus === "stale") {
|
|
1227
|
+
await staleHandlingPromise;
|
|
1228
|
+
await this.releaseExecutionGuardLease(executionGuardLease, jobToRun, context);
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
try {
|
|
1233
|
+
if (!config.executionGuard) {
|
|
1234
|
+
updateOrionAsyncContext({
|
|
1235
|
+
jobName: jobToRun.name,
|
|
1236
|
+
jobId: jobToRun.jobId,
|
|
1237
|
+
params: jobToRun.params
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
const result = executionGuardLease ? await this.runGuardedResolver(job, jobToRun, context, executionGuardLease) : await job.resolve(jobToRun.params, context);
|
|
1108
1241
|
context.clearStaleTimeout();
|
|
1109
1242
|
if (executionStatus === "stale") {
|
|
1110
1243
|
await staleHandlingPromise;
|
|
@@ -1283,7 +1416,11 @@ var WorkerService = class {
|
|
|
1283
1416
|
jobs: config.jobs,
|
|
1284
1417
|
maxTries: config.maxTries,
|
|
1285
1418
|
maxTriesReachedRetentionMs: config.maxTriesReachedRetentionMs,
|
|
1286
|
-
onMaxTriesReached: config.onMaxTriesReached
|
|
1419
|
+
onMaxTriesReached: config.onMaxTriesReached,
|
|
1420
|
+
...config.executionGuard ? {
|
|
1421
|
+
executionGuard: config.executionGuard,
|
|
1422
|
+
executionGuardRetryDelayMs: Math.max(config.pollInterval, 1e3)
|
|
1423
|
+
} : {}
|
|
1287
1424
|
};
|
|
1288
1425
|
logger4.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
|
|
1289
1426
|
let consecutivePartitionMisses = 0;
|