@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 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: () => clearTimeout(staleTimeout),
910
- extendLockTime: async (extraTime) => {
911
- var _a2;
912
- if (!isExecutionActive()) {
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
- if (!didExtend) {
925
- onStale();
926
- return;
998
+ pendingExtraTime = Math.max(pendingExtraTime ?? extraTime, extraTime);
999
+ if (!renewalRunning) {
1000
+ renewalRunning = true;
1001
+ renewalPromise = runRenewals();
927
1002
  }
928
- staleTimeout = setTimeout(() => onStale(), extraTime);
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
- try {
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
- const result = await job.resolve(jobToRun.params, context);
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;