@orion-js/dogs 4.6.8 → 4.6.9

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.js CHANGED
@@ -839,7 +839,7 @@ function scheduleJobsInternal(jobs, nPartitions) {
839
839
 
840
840
  // src/services/WorkerService.ts
841
841
  import { sleep } from "@orion-js/helpers";
842
- import { logger as logger4 } from "@orion-js/logger";
842
+ import { logger as logger5 } from "@orion-js/logger";
843
843
  import { Inject as Inject3, Service as Service3 } from "@orion-js/services";
844
844
 
845
845
  // src/services/Executor.ts
@@ -1306,6 +1306,76 @@ __decorateElement(_init4, 5, "jobsHistoryRepo", _jobsHistoryRepo_dec, Executor);
1306
1306
  Executor = __decorateElement(_init4, 0, "Executor", _Executor_decorators, Executor);
1307
1307
  __runInitializers(_init4, 1, Executor);
1308
1308
 
1309
+ // src/services/jobFilters.ts
1310
+ import { logger as logger4 } from "@orion-js/logger";
1311
+ var DEFAULT_JOBS_FILTER_TIMEOUT_MS = 5e3;
1312
+ var warnedUnknownJobNames = /* @__PURE__ */ new Set();
1313
+ async function withTimeout(promise, timeoutMs, name) {
1314
+ let timer;
1315
+ const timeout = new Promise((_, reject) => {
1316
+ timer = setTimeout(() => reject(new Error(`${name} timed out after ${timeoutMs}ms`)), timeoutMs);
1317
+ });
1318
+ try {
1319
+ return await Promise.race([promise, timeout]);
1320
+ } finally {
1321
+ clearTimeout(timer);
1322
+ }
1323
+ }
1324
+ async function runCheck(options, name, run) {
1325
+ if (options.pendingChecks.has(name)) {
1326
+ throw new Error(`${name} is still running from a previous claim`);
1327
+ }
1328
+ const promise = Promise.resolve().then(run);
1329
+ options.pendingChecks.set(name, promise);
1330
+ void promise.then(
1331
+ () => options.pendingChecks.delete(name),
1332
+ () => options.pendingChecks.delete(name)
1333
+ );
1334
+ return await withTimeout(promise, options.timeoutMs, name);
1335
+ }
1336
+ function warnUnknownJobNames(jobs, filter) {
1337
+ for (const jobName of [...filter.allow ?? [], ...filter.deny ?? []]) {
1338
+ if (jobs[jobName] || warnedUnknownJobNames.has(jobName)) continue;
1339
+ warnedUnknownJobNames.add(jobName);
1340
+ logger4.warn(`getJobsFilter returned unknown job name "${jobName}"; it will be ignored.`);
1341
+ }
1342
+ }
1343
+ async function filterByJobsFilter(options, getJobsFilter) {
1344
+ try {
1345
+ const filter = await runCheck(options, "getJobsFilter", getJobsFilter) ?? {};
1346
+ warnUnknownJobNames(options.jobs, filter);
1347
+ return options.jobNames.filter((jobName) => {
1348
+ var _a;
1349
+ if (filter.allow && !filter.allow.includes(jobName)) return false;
1350
+ if ((_a = filter.deny) == null ? void 0 : _a.includes(jobName)) return false;
1351
+ return true;
1352
+ });
1353
+ } catch (error) {
1354
+ logger4.error("Error in getJobsFilter; no jobs will be taken on this claim.", { error });
1355
+ return [];
1356
+ }
1357
+ }
1358
+ async function canTakeJob(options, jobName) {
1359
+ const canTakeJobs = options.jobs[jobName].canTakeJobs;
1360
+ if (!canTakeJobs) return true;
1361
+ try {
1362
+ return await runCheck(options, `canTakeJobs of "${jobName}"`, canTakeJobs) === true;
1363
+ } catch (error) {
1364
+ logger4.error(`Error in canTakeJobs of job "${jobName}"; it will not be taken.`, { error });
1365
+ return false;
1366
+ }
1367
+ }
1368
+ async function getTakeableJobNames(inputOptions) {
1369
+ const options = {
1370
+ timeoutMs: DEFAULT_JOBS_FILTER_TIMEOUT_MS,
1371
+ pendingChecks: /* @__PURE__ */ new Map(),
1372
+ ...inputOptions
1373
+ };
1374
+ const jobNames = inputOptions.getJobsFilter ? await filterByJobsFilter(options, inputOptions.getJobsFilter) : options.jobNames;
1375
+ const checks = await Promise.all(jobNames.map((jobName) => canTakeJob(options, jobName)));
1376
+ return jobNames.filter((_, index) => checks[index]);
1377
+ }
1378
+
1309
1379
  // src/services/WorkerService.ts
1310
1380
  var FILTERED_JOB_NAMES_ACQUISITION_HINT = "byJobName";
1311
1381
  var _executor_dec, _jobsRepo_dec3, _WorkerService_decorators, _init5;
@@ -1317,6 +1387,7 @@ var WorkerService = class {
1317
1387
  __publicField(this, "partitionReconcilerJitterMs", 30 * 1e3);
1318
1388
  __publicField(this, "partitionReconcilerBatchPauseMs", 250);
1319
1389
  __publicField(this, "partitionReconcilerBatchSize", 500);
1390
+ __publicField(this, "jobsFilterTimeoutMs", DEFAULT_JOBS_FILTER_TIMEOUT_MS);
1320
1391
  __publicField(this, "jobsRepo", __runInitializers(_init5, 8, this)), __runInitializers(_init5, 11, this);
1321
1392
  __publicField(this, "executor", __runInitializers(_init5, 12, this)), __runInitializers(_init5, 15, this);
1322
1393
  }
@@ -1375,13 +1446,13 @@ var WorkerService = class {
1375
1446
  };
1376
1447
  this.reserveJobExecution(workersInstance, jobToRun.name);
1377
1448
  workersInstance.activeExecutions.set(jobToRun.executionId, execution);
1378
- logger4.debug(`Starting job execution "${jobToRun.executionId}"`, jobToRun);
1449
+ logger5.debug(`Starting job execution "${jobToRun.executionId}"`, jobToRun);
1379
1450
  const executionPromise = this.executor.executeJob(config, jobToRun, () => {
1380
1451
  this.releaseExecutionCapacity(workersInstance, execution, "stale");
1381
1452
  });
1382
1453
  execution.promise = executionPromise;
1383
1454
  void executionPromise.catch((error) => {
1384
- logger4.error(`Unhandled error executing job "${jobToRun.name}"`, { error });
1455
+ logger5.error(`Unhandled error executing job "${jobToRun.name}"`, { error });
1385
1456
  }).finally(() => {
1386
1457
  if (execution.status === "running") {
1387
1458
  this.releaseExecutionCapacity(workersInstance, execution, "finished");
@@ -1422,18 +1493,33 @@ var WorkerService = class {
1422
1493
  executionGuardRetryDelayMs: Math.max(config.pollInterval, 1e3)
1423
1494
  } : {}
1424
1495
  };
1425
- logger4.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
1496
+ logger5.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
1426
1497
  let consecutivePartitionMisses = 0;
1427
1498
  while (workersInstance.running) {
1428
1499
  let didMiss = false;
1429
1500
  let allJobNamesAtCapacity = false;
1430
1501
  try {
1431
1502
  while (workersInstance.activeExecutions.size < config.workersCount) {
1432
- const availableJobNames = this.getAvailableJobNames(config, workersInstance, jobNames);
1433
- if (availableJobNames.length === 0) {
1503
+ const jobNamesWithCapacity = this.getAvailableJobNames(config, workersInstance, jobNames);
1504
+ if (jobNamesWithCapacity.length === 0) {
1434
1505
  allJobNamesAtCapacity = workersInstance.activeExecutions.size > 0;
1435
1506
  break;
1436
1507
  }
1508
+ const availableJobNames = await Promise.race([
1509
+ getTakeableJobNames({
1510
+ jobs: config.jobs,
1511
+ getJobsFilter: config.getJobsFilter,
1512
+ jobNames: jobNamesWithCapacity,
1513
+ timeoutMs: this.jobsFilterTimeoutMs,
1514
+ pendingChecks: workersInstance.pendingFilterChecks
1515
+ }),
1516
+ workersInstance.stopSignal.then(() => [])
1517
+ ]);
1518
+ if (!workersInstance.running) break;
1519
+ if (availableJobNames.length === 0) {
1520
+ didMiss = true;
1521
+ break;
1522
+ }
1437
1523
  const jobAcquisitionHint = availableJobNames.length < jobNames.length ? FILTERED_JOB_NAMES_ACQUISITION_HINT : workersInstance.jobAcquisitionHint;
1438
1524
  const partition = this.getNextPartition(config, workersInstance);
1439
1525
  const jobToRun = await this.jobsRepo.getJobAndLock(
@@ -1444,7 +1530,7 @@ var WorkerService = class {
1444
1530
  );
1445
1531
  if (!jobToRun) {
1446
1532
  consecutivePartitionMisses++;
1447
- logger4.debug(`No job to run in partition ${partition}`);
1533
+ logger5.debug(`No job to run in partition ${partition}`);
1448
1534
  if (consecutivePartitionMisses >= config.nPartitions) {
1449
1535
  consecutivePartitionMisses = 0;
1450
1536
  didMiss = true;
@@ -1457,7 +1543,7 @@ var WorkerService = class {
1457
1543
  if (!workersInstance.running) break;
1458
1544
  }
1459
1545
  } catch (error) {
1460
- logger4.error("Error in job scheduler.", { error });
1546
+ logger5.error("Error in job scheduler.", { error });
1461
1547
  consecutivePartitionMisses = 0;
1462
1548
  didMiss = true;
1463
1549
  }
@@ -1509,7 +1595,7 @@ var WorkerService = class {
1509
1595
  } catch (error) {
1510
1596
  this.resetPendingJobAcquisitionHint(workersInstance);
1511
1597
  if (workersInstance.running) {
1512
- logger4.warn("Job acquisition hint probe failed; keeping the current hint.", { error });
1598
+ logger5.warn("Job acquisition hint probe failed; keeping the current hint.", { error });
1513
1599
  }
1514
1600
  return;
1515
1601
  }
@@ -1522,12 +1608,12 @@ var WorkerService = class {
1522
1608
  workersInstance.hasCompletedInitialJobAcquisitionHintProbe = true;
1523
1609
  workersInstance.jobAcquisitionHint = winner;
1524
1610
  this.resetPendingJobAcquisitionHint(workersInstance);
1525
- logger4.info(`Selected job acquisition hint "${winner}" after startup probe.`);
1611
+ logger5.info(`Selected job acquisition hint "${winner}" after startup probe.`);
1526
1612
  return;
1527
1613
  }
1528
1614
  if (winner === currentHint) {
1529
1615
  this.resetPendingJobAcquisitionHint(workersInstance);
1530
- logger4.debug(`Keeping job acquisition hint "${currentHint}" after probe.`);
1616
+ logger5.debug(`Keeping job acquisition hint "${currentHint}" after probe.`);
1531
1617
  return;
1532
1618
  }
1533
1619
  if (workersInstance.pendingJobAcquisitionHint === winner) {
@@ -1537,14 +1623,14 @@ var WorkerService = class {
1537
1623
  workersInstance.pendingJobAcquisitionHintWins = 1;
1538
1624
  }
1539
1625
  if (workersInstance.pendingJobAcquisitionHintWins < 2) {
1540
- logger4.debug(
1626
+ logger5.debug(
1541
1627
  `Keeping job acquisition hint "${currentHint}"; "${winner}" has one consecutive probe win.`
1542
1628
  );
1543
1629
  return;
1544
1630
  }
1545
1631
  workersInstance.jobAcquisitionHint = winner;
1546
1632
  this.resetPendingJobAcquisitionHint(workersInstance);
1547
- logger4.info(`Changed job acquisition hint from "${currentHint}" to "${winner}".`);
1633
+ logger5.info(`Changed job acquisition hint from "${currentHint}" to "${winner}".`);
1548
1634
  }
1549
1635
  async waitForNextJobAcquisitionHintProbe(workersInstance) {
1550
1636
  await new Promise((resolve) => {
@@ -1581,7 +1667,7 @@ var WorkerService = class {
1581
1667
  batchSize: this.partitionReconcilerBatchSize
1582
1668
  });
1583
1669
  } catch (error) {
1584
- logger4.warn("Job partition reconciliation failed; it will be retried.", { error });
1670
+ logger5.warn("Job partition reconciliation failed; it will be retried.", { error });
1585
1671
  return 0;
1586
1672
  }
1587
1673
  }
@@ -1621,6 +1707,7 @@ var WorkerService = class {
1621
1707
  activeExecutions: /* @__PURE__ */ new Map(),
1622
1708
  staleExecutions: /* @__PURE__ */ new Map(),
1623
1709
  runningJobsByName: /* @__PURE__ */ new Map(),
1710
+ pendingFilterChecks: /* @__PURE__ */ new Map(),
1624
1711
  jobAcquisitionHint: INITIAL_JOB_ACQUISITION_HINT,
1625
1712
  jobAcquisitionHintProbeCycle: 0,
1626
1713
  hasCompletedInitialJobAcquisitionHintProbe: false,
@@ -1639,7 +1726,7 @@ var WorkerService = class {
1639
1726
  if (workersInstance.stopPromise) return workersInstance.stopPromise;
1640
1727
  workersInstance.stopPromise = (async () => {
1641
1728
  var _a, _b;
1642
- logger4.info("Stopping job scheduler...");
1729
+ logger5.info("Stopping job scheduler...");
1643
1730
  workersInstance.running = false;
1644
1731
  workersInstance.signalStop();
1645
1732
  (_a = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a.call(workersInstance);
@@ -1664,17 +1751,17 @@ var WorkerService = class {
1664
1751
  const jobs = this.getJobs(config.jobs);
1665
1752
  await Promise.all(
1666
1753
  jobs.filter((job) => job.type === "recurrent").map(async (job) => {
1667
- logger4.debug(`Ensuring records for job "${job.name}"...`);
1754
+ logger5.debug(`Ensuring records for job "${job.name}"...`);
1668
1755
  await this.jobsRepo.ensureJobRecord(job, getRandomPartition(config.nPartitions));
1669
1756
  })
1670
1757
  );
1671
1758
  }
1672
1759
  async runScheduler(config, workersInstance) {
1673
- logger4.debug("Will ensure maxTriesReached retention and TTL index");
1674
- void this.jobsRepo.ensureMaxTriesReachedRetention(config.maxTriesReachedRetentionMs).catch((error) => logger4.error("Error ensuring maxTriesReached retention", { error }));
1675
- logger4.debug("Will ensure records for recurrent jobs");
1760
+ logger5.debug("Will ensure maxTriesReached retention and TTL index");
1761
+ void this.jobsRepo.ensureMaxTriesReachedRetention(config.maxTriesReachedRetentionMs).catch((error) => logger5.error("Error ensuring maxTriesReached retention", { error }));
1762
+ logger5.debug("Will ensure records for recurrent jobs");
1676
1763
  await this.ensureRecords(config);
1677
- logger4.info(`Starting job scheduler with concurrency ${config.workersCount}`);
1764
+ logger5.info(`Starting job scheduler with concurrency ${config.workersCount}`);
1678
1765
  await this.runSchedulerLoop(config, workersInstance);
1679
1766
  }
1680
1767
  /**
@@ -1700,7 +1787,7 @@ var WorkerService = class {
1700
1787
  setNameToJobs(config.jobs, config.nPartitions);
1701
1788
  (_b = (_a = this.jobsRepo).configureJobPartitions) == null ? void 0 : _b.call(_a, this.getJobNames(config.jobs), config.nPartitions);
1702
1789
  const workersInstance = this.createWorkersInstanceDefinition(config);
1703
- logger4.debug("Starting job scheduler", config);
1790
+ logger5.debug("Starting job scheduler", config);
1704
1791
  workersInstance.jobAcquisitionHintProbePromise = this.runJobAcquisitionHintProbeLoop(
1705
1792
  config,
1706
1793
  workersInstance
@@ -1717,7 +1804,7 @@ var WorkerService = class {
1717
1804
  workersInstance.signalStop();
1718
1805
  (_a2 = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a2.call(workersInstance);
1719
1806
  (_b2 = workersInstance.cancelPartitionReconcilerWait) == null ? void 0 : _b2.call(workersInstance);
1720
- logger4.error("Job scheduler stopped after an unexpected error.", { error });
1807
+ logger5.error("Job scheduler stopped after an unexpected error.", { error });
1721
1808
  });
1722
1809
  return workersInstance;
1723
1810
  }