@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.cjs +109 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +109 -22
- package/dist/index.js.map +1 -1
- package/dist/services/WorkerService.d.ts +3 -0
- package/dist/services/jobFilters.d.ts +21 -0
- package/dist/types/JobsDefinition.d.ts +7 -0
- package/dist/types/StartConfig.d.ts +21 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -883,7 +883,7 @@ function scheduleJobsInternal(jobs, nPartitions) {
|
|
|
883
883
|
|
|
884
884
|
// src/services/WorkerService.ts
|
|
885
885
|
var import_helpers2 = require("@orion-js/helpers");
|
|
886
|
-
var
|
|
886
|
+
var import_logger5 = require("@orion-js/logger");
|
|
887
887
|
var import_services4 = require("@orion-js/services");
|
|
888
888
|
|
|
889
889
|
// src/services/Executor.ts
|
|
@@ -1350,6 +1350,76 @@ __decorateElement(_init4, 5, "jobsHistoryRepo", _jobsHistoryRepo_dec, Executor);
|
|
|
1350
1350
|
Executor = __decorateElement(_init4, 0, "Executor", _Executor_decorators, Executor);
|
|
1351
1351
|
__runInitializers(_init4, 1, Executor);
|
|
1352
1352
|
|
|
1353
|
+
// src/services/jobFilters.ts
|
|
1354
|
+
var import_logger4 = require("@orion-js/logger");
|
|
1355
|
+
var DEFAULT_JOBS_FILTER_TIMEOUT_MS = 5e3;
|
|
1356
|
+
var warnedUnknownJobNames = /* @__PURE__ */ new Set();
|
|
1357
|
+
async function withTimeout(promise, timeoutMs, name) {
|
|
1358
|
+
let timer;
|
|
1359
|
+
const timeout = new Promise((_, reject) => {
|
|
1360
|
+
timer = setTimeout(() => reject(new Error(`${name} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
1361
|
+
});
|
|
1362
|
+
try {
|
|
1363
|
+
return await Promise.race([promise, timeout]);
|
|
1364
|
+
} finally {
|
|
1365
|
+
clearTimeout(timer);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
async function runCheck(options, name, run) {
|
|
1369
|
+
if (options.pendingChecks.has(name)) {
|
|
1370
|
+
throw new Error(`${name} is still running from a previous claim`);
|
|
1371
|
+
}
|
|
1372
|
+
const promise = Promise.resolve().then(run);
|
|
1373
|
+
options.pendingChecks.set(name, promise);
|
|
1374
|
+
void promise.then(
|
|
1375
|
+
() => options.pendingChecks.delete(name),
|
|
1376
|
+
() => options.pendingChecks.delete(name)
|
|
1377
|
+
);
|
|
1378
|
+
return await withTimeout(promise, options.timeoutMs, name);
|
|
1379
|
+
}
|
|
1380
|
+
function warnUnknownJobNames(jobs, filter) {
|
|
1381
|
+
for (const jobName of [...filter.allow ?? [], ...filter.deny ?? []]) {
|
|
1382
|
+
if (jobs[jobName] || warnedUnknownJobNames.has(jobName)) continue;
|
|
1383
|
+
warnedUnknownJobNames.add(jobName);
|
|
1384
|
+
import_logger4.logger.warn(`getJobsFilter returned unknown job name "${jobName}"; it will be ignored.`);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
async function filterByJobsFilter(options, getJobsFilter) {
|
|
1388
|
+
try {
|
|
1389
|
+
const filter = await runCheck(options, "getJobsFilter", getJobsFilter) ?? {};
|
|
1390
|
+
warnUnknownJobNames(options.jobs, filter);
|
|
1391
|
+
return options.jobNames.filter((jobName) => {
|
|
1392
|
+
var _a;
|
|
1393
|
+
if (filter.allow && !filter.allow.includes(jobName)) return false;
|
|
1394
|
+
if ((_a = filter.deny) == null ? void 0 : _a.includes(jobName)) return false;
|
|
1395
|
+
return true;
|
|
1396
|
+
});
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
import_logger4.logger.error("Error in getJobsFilter; no jobs will be taken on this claim.", { error });
|
|
1399
|
+
return [];
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
async function canTakeJob(options, jobName) {
|
|
1403
|
+
const canTakeJobs = options.jobs[jobName].canTakeJobs;
|
|
1404
|
+
if (!canTakeJobs) return true;
|
|
1405
|
+
try {
|
|
1406
|
+
return await runCheck(options, `canTakeJobs of "${jobName}"`, canTakeJobs) === true;
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
import_logger4.logger.error(`Error in canTakeJobs of job "${jobName}"; it will not be taken.`, { error });
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
async function getTakeableJobNames(inputOptions) {
|
|
1413
|
+
const options = {
|
|
1414
|
+
timeoutMs: DEFAULT_JOBS_FILTER_TIMEOUT_MS,
|
|
1415
|
+
pendingChecks: /* @__PURE__ */ new Map(),
|
|
1416
|
+
...inputOptions
|
|
1417
|
+
};
|
|
1418
|
+
const jobNames = inputOptions.getJobsFilter ? await filterByJobsFilter(options, inputOptions.getJobsFilter) : options.jobNames;
|
|
1419
|
+
const checks = await Promise.all(jobNames.map((jobName) => canTakeJob(options, jobName)));
|
|
1420
|
+
return jobNames.filter((_, index) => checks[index]);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1353
1423
|
// src/services/WorkerService.ts
|
|
1354
1424
|
var FILTERED_JOB_NAMES_ACQUISITION_HINT = "byJobName";
|
|
1355
1425
|
var _executor_dec, _jobsRepo_dec3, _WorkerService_decorators, _init5;
|
|
@@ -1361,6 +1431,7 @@ var WorkerService = class {
|
|
|
1361
1431
|
__publicField(this, "partitionReconcilerJitterMs", 30 * 1e3);
|
|
1362
1432
|
__publicField(this, "partitionReconcilerBatchPauseMs", 250);
|
|
1363
1433
|
__publicField(this, "partitionReconcilerBatchSize", 500);
|
|
1434
|
+
__publicField(this, "jobsFilterTimeoutMs", DEFAULT_JOBS_FILTER_TIMEOUT_MS);
|
|
1364
1435
|
__publicField(this, "jobsRepo", __runInitializers(_init5, 8, this)), __runInitializers(_init5, 11, this);
|
|
1365
1436
|
__publicField(this, "executor", __runInitializers(_init5, 12, this)), __runInitializers(_init5, 15, this);
|
|
1366
1437
|
}
|
|
@@ -1419,13 +1490,13 @@ var WorkerService = class {
|
|
|
1419
1490
|
};
|
|
1420
1491
|
this.reserveJobExecution(workersInstance, jobToRun.name);
|
|
1421
1492
|
workersInstance.activeExecutions.set(jobToRun.executionId, execution);
|
|
1422
|
-
|
|
1493
|
+
import_logger5.logger.debug(`Starting job execution "${jobToRun.executionId}"`, jobToRun);
|
|
1423
1494
|
const executionPromise = this.executor.executeJob(config, jobToRun, () => {
|
|
1424
1495
|
this.releaseExecutionCapacity(workersInstance, execution, "stale");
|
|
1425
1496
|
});
|
|
1426
1497
|
execution.promise = executionPromise;
|
|
1427
1498
|
void executionPromise.catch((error) => {
|
|
1428
|
-
|
|
1499
|
+
import_logger5.logger.error(`Unhandled error executing job "${jobToRun.name}"`, { error });
|
|
1429
1500
|
}).finally(() => {
|
|
1430
1501
|
if (execution.status === "running") {
|
|
1431
1502
|
this.releaseExecutionCapacity(workersInstance, execution, "finished");
|
|
@@ -1466,18 +1537,33 @@ var WorkerService = class {
|
|
|
1466
1537
|
executionGuardRetryDelayMs: Math.max(config.pollInterval, 1e3)
|
|
1467
1538
|
} : {}
|
|
1468
1539
|
};
|
|
1469
|
-
|
|
1540
|
+
import_logger5.logger.debug(`Running scheduler loop for jobs "${jobNames.join(", ")}"...`);
|
|
1470
1541
|
let consecutivePartitionMisses = 0;
|
|
1471
1542
|
while (workersInstance.running) {
|
|
1472
1543
|
let didMiss = false;
|
|
1473
1544
|
let allJobNamesAtCapacity = false;
|
|
1474
1545
|
try {
|
|
1475
1546
|
while (workersInstance.activeExecutions.size < config.workersCount) {
|
|
1476
|
-
const
|
|
1477
|
-
if (
|
|
1547
|
+
const jobNamesWithCapacity = this.getAvailableJobNames(config, workersInstance, jobNames);
|
|
1548
|
+
if (jobNamesWithCapacity.length === 0) {
|
|
1478
1549
|
allJobNamesAtCapacity = workersInstance.activeExecutions.size > 0;
|
|
1479
1550
|
break;
|
|
1480
1551
|
}
|
|
1552
|
+
const availableJobNames = await Promise.race([
|
|
1553
|
+
getTakeableJobNames({
|
|
1554
|
+
jobs: config.jobs,
|
|
1555
|
+
getJobsFilter: config.getJobsFilter,
|
|
1556
|
+
jobNames: jobNamesWithCapacity,
|
|
1557
|
+
timeoutMs: this.jobsFilterTimeoutMs,
|
|
1558
|
+
pendingChecks: workersInstance.pendingFilterChecks
|
|
1559
|
+
}),
|
|
1560
|
+
workersInstance.stopSignal.then(() => [])
|
|
1561
|
+
]);
|
|
1562
|
+
if (!workersInstance.running) break;
|
|
1563
|
+
if (availableJobNames.length === 0) {
|
|
1564
|
+
didMiss = true;
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1481
1567
|
const jobAcquisitionHint = availableJobNames.length < jobNames.length ? FILTERED_JOB_NAMES_ACQUISITION_HINT : workersInstance.jobAcquisitionHint;
|
|
1482
1568
|
const partition = this.getNextPartition(config, workersInstance);
|
|
1483
1569
|
const jobToRun = await this.jobsRepo.getJobAndLock(
|
|
@@ -1488,7 +1574,7 @@ var WorkerService = class {
|
|
|
1488
1574
|
);
|
|
1489
1575
|
if (!jobToRun) {
|
|
1490
1576
|
consecutivePartitionMisses++;
|
|
1491
|
-
|
|
1577
|
+
import_logger5.logger.debug(`No job to run in partition ${partition}`);
|
|
1492
1578
|
if (consecutivePartitionMisses >= config.nPartitions) {
|
|
1493
1579
|
consecutivePartitionMisses = 0;
|
|
1494
1580
|
didMiss = true;
|
|
@@ -1501,7 +1587,7 @@ var WorkerService = class {
|
|
|
1501
1587
|
if (!workersInstance.running) break;
|
|
1502
1588
|
}
|
|
1503
1589
|
} catch (error) {
|
|
1504
|
-
|
|
1590
|
+
import_logger5.logger.error("Error in job scheduler.", { error });
|
|
1505
1591
|
consecutivePartitionMisses = 0;
|
|
1506
1592
|
didMiss = true;
|
|
1507
1593
|
}
|
|
@@ -1553,7 +1639,7 @@ var WorkerService = class {
|
|
|
1553
1639
|
} catch (error) {
|
|
1554
1640
|
this.resetPendingJobAcquisitionHint(workersInstance);
|
|
1555
1641
|
if (workersInstance.running) {
|
|
1556
|
-
|
|
1642
|
+
import_logger5.logger.warn("Job acquisition hint probe failed; keeping the current hint.", { error });
|
|
1557
1643
|
}
|
|
1558
1644
|
return;
|
|
1559
1645
|
}
|
|
@@ -1566,12 +1652,12 @@ var WorkerService = class {
|
|
|
1566
1652
|
workersInstance.hasCompletedInitialJobAcquisitionHintProbe = true;
|
|
1567
1653
|
workersInstance.jobAcquisitionHint = winner;
|
|
1568
1654
|
this.resetPendingJobAcquisitionHint(workersInstance);
|
|
1569
|
-
|
|
1655
|
+
import_logger5.logger.info(`Selected job acquisition hint "${winner}" after startup probe.`);
|
|
1570
1656
|
return;
|
|
1571
1657
|
}
|
|
1572
1658
|
if (winner === currentHint) {
|
|
1573
1659
|
this.resetPendingJobAcquisitionHint(workersInstance);
|
|
1574
|
-
|
|
1660
|
+
import_logger5.logger.debug(`Keeping job acquisition hint "${currentHint}" after probe.`);
|
|
1575
1661
|
return;
|
|
1576
1662
|
}
|
|
1577
1663
|
if (workersInstance.pendingJobAcquisitionHint === winner) {
|
|
@@ -1581,14 +1667,14 @@ var WorkerService = class {
|
|
|
1581
1667
|
workersInstance.pendingJobAcquisitionHintWins = 1;
|
|
1582
1668
|
}
|
|
1583
1669
|
if (workersInstance.pendingJobAcquisitionHintWins < 2) {
|
|
1584
|
-
|
|
1670
|
+
import_logger5.logger.debug(
|
|
1585
1671
|
`Keeping job acquisition hint "${currentHint}"; "${winner}" has one consecutive probe win.`
|
|
1586
1672
|
);
|
|
1587
1673
|
return;
|
|
1588
1674
|
}
|
|
1589
1675
|
workersInstance.jobAcquisitionHint = winner;
|
|
1590
1676
|
this.resetPendingJobAcquisitionHint(workersInstance);
|
|
1591
|
-
|
|
1677
|
+
import_logger5.logger.info(`Changed job acquisition hint from "${currentHint}" to "${winner}".`);
|
|
1592
1678
|
}
|
|
1593
1679
|
async waitForNextJobAcquisitionHintProbe(workersInstance) {
|
|
1594
1680
|
await new Promise((resolve) => {
|
|
@@ -1625,7 +1711,7 @@ var WorkerService = class {
|
|
|
1625
1711
|
batchSize: this.partitionReconcilerBatchSize
|
|
1626
1712
|
});
|
|
1627
1713
|
} catch (error) {
|
|
1628
|
-
|
|
1714
|
+
import_logger5.logger.warn("Job partition reconciliation failed; it will be retried.", { error });
|
|
1629
1715
|
return 0;
|
|
1630
1716
|
}
|
|
1631
1717
|
}
|
|
@@ -1665,6 +1751,7 @@ var WorkerService = class {
|
|
|
1665
1751
|
activeExecutions: /* @__PURE__ */ new Map(),
|
|
1666
1752
|
staleExecutions: /* @__PURE__ */ new Map(),
|
|
1667
1753
|
runningJobsByName: /* @__PURE__ */ new Map(),
|
|
1754
|
+
pendingFilterChecks: /* @__PURE__ */ new Map(),
|
|
1668
1755
|
jobAcquisitionHint: INITIAL_JOB_ACQUISITION_HINT,
|
|
1669
1756
|
jobAcquisitionHintProbeCycle: 0,
|
|
1670
1757
|
hasCompletedInitialJobAcquisitionHintProbe: false,
|
|
@@ -1683,7 +1770,7 @@ var WorkerService = class {
|
|
|
1683
1770
|
if (workersInstance.stopPromise) return workersInstance.stopPromise;
|
|
1684
1771
|
workersInstance.stopPromise = (async () => {
|
|
1685
1772
|
var _a, _b;
|
|
1686
|
-
|
|
1773
|
+
import_logger5.logger.info("Stopping job scheduler...");
|
|
1687
1774
|
workersInstance.running = false;
|
|
1688
1775
|
workersInstance.signalStop();
|
|
1689
1776
|
(_a = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a.call(workersInstance);
|
|
@@ -1708,17 +1795,17 @@ var WorkerService = class {
|
|
|
1708
1795
|
const jobs = this.getJobs(config.jobs);
|
|
1709
1796
|
await Promise.all(
|
|
1710
1797
|
jobs.filter((job) => job.type === "recurrent").map(async (job) => {
|
|
1711
|
-
|
|
1798
|
+
import_logger5.logger.debug(`Ensuring records for job "${job.name}"...`);
|
|
1712
1799
|
await this.jobsRepo.ensureJobRecord(job, getRandomPartition(config.nPartitions));
|
|
1713
1800
|
})
|
|
1714
1801
|
);
|
|
1715
1802
|
}
|
|
1716
1803
|
async runScheduler(config, workersInstance) {
|
|
1717
|
-
|
|
1718
|
-
void this.jobsRepo.ensureMaxTriesReachedRetention(config.maxTriesReachedRetentionMs).catch((error) =>
|
|
1719
|
-
|
|
1804
|
+
import_logger5.logger.debug("Will ensure maxTriesReached retention and TTL index");
|
|
1805
|
+
void this.jobsRepo.ensureMaxTriesReachedRetention(config.maxTriesReachedRetentionMs).catch((error) => import_logger5.logger.error("Error ensuring maxTriesReached retention", { error }));
|
|
1806
|
+
import_logger5.logger.debug("Will ensure records for recurrent jobs");
|
|
1720
1807
|
await this.ensureRecords(config);
|
|
1721
|
-
|
|
1808
|
+
import_logger5.logger.info(`Starting job scheduler with concurrency ${config.workersCount}`);
|
|
1722
1809
|
await this.runSchedulerLoop(config, workersInstance);
|
|
1723
1810
|
}
|
|
1724
1811
|
/**
|
|
@@ -1744,7 +1831,7 @@ var WorkerService = class {
|
|
|
1744
1831
|
setNameToJobs(config.jobs, config.nPartitions);
|
|
1745
1832
|
(_b = (_a = this.jobsRepo).configureJobPartitions) == null ? void 0 : _b.call(_a, this.getJobNames(config.jobs), config.nPartitions);
|
|
1746
1833
|
const workersInstance = this.createWorkersInstanceDefinition(config);
|
|
1747
|
-
|
|
1834
|
+
import_logger5.logger.debug("Starting job scheduler", config);
|
|
1748
1835
|
workersInstance.jobAcquisitionHintProbePromise = this.runJobAcquisitionHintProbeLoop(
|
|
1749
1836
|
config,
|
|
1750
1837
|
workersInstance
|
|
@@ -1761,7 +1848,7 @@ var WorkerService = class {
|
|
|
1761
1848
|
workersInstance.signalStop();
|
|
1762
1849
|
(_a2 = workersInstance.cancelJobAcquisitionHintProbeWait) == null ? void 0 : _a2.call(workersInstance);
|
|
1763
1850
|
(_b2 = workersInstance.cancelPartitionReconcilerWait) == null ? void 0 : _b2.call(workersInstance);
|
|
1764
|
-
|
|
1851
|
+
import_logger5.logger.error("Job scheduler stopped after an unexpected error.", { error });
|
|
1765
1852
|
});
|
|
1766
1853
|
return workersInstance;
|
|
1767
1854
|
}
|