@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.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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1433
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1674
|
-
void this.jobsRepo.ensureMaxTriesReachedRetention(config.maxTriesReachedRetentionMs).catch((error) =>
|
|
1675
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1807
|
+
logger5.error("Job scheduler stopped after an unexpected error.", { error });
|
|
1721
1808
|
});
|
|
1722
1809
|
return workersInstance;
|
|
1723
1810
|
}
|