codemem 0.46.0-rc.1 → 0.46.0-rc.3
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 +57 -10
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -4463,12 +4463,40 @@ function readWorkerSyncConfig(configPath) {
|
|
|
4463
4463
|
const config = configPath ? readCodememConfigFileAtPath(configPath) : readCodememConfigFile();
|
|
4464
4464
|
return readCoordinatorSyncConfig(config);
|
|
4465
4465
|
}
|
|
4466
|
+
var PENDING_RECHECK_INTERVAL_MS = 3e4;
|
|
4467
|
+
var BACKFILL_POLL_INTERVAL_MS = 1e3;
|
|
4468
|
+
/**
|
|
4469
|
+
* Decide when the active backfill is done. A backfill can complete while its
|
|
4470
|
+
* predicate still reports work (for example rows it skipped), so a terminal
|
|
4471
|
+
* row written during this run ends it; rows finished before the run started
|
|
4472
|
+
* do not count.
|
|
4473
|
+
*/
|
|
4474
|
+
function createBackfillCompletionTracker(store) {
|
|
4475
|
+
let startedAt = "";
|
|
4476
|
+
let lastPendingCheckAt = 0;
|
|
4477
|
+
return {
|
|
4478
|
+
begin(plan) {
|
|
4479
|
+
startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4480
|
+
lastPendingCheckAt = Date.now();
|
|
4481
|
+
return plan;
|
|
4482
|
+
},
|
|
4483
|
+
finished(plan, job) {
|
|
4484
|
+
if (job && (job.status === "completed" || job.status === "cancelled")) {
|
|
4485
|
+
if ((job.finished_at ?? job.updated_at) >= startedAt) return true;
|
|
4486
|
+
}
|
|
4487
|
+
if (Date.now() - lastPendingCheckAt < PENDING_RECHECK_INTERVAL_MS) return false;
|
|
4488
|
+
lastPendingCheckAt = Date.now();
|
|
4489
|
+
return !plan.isPending(store.db);
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
}
|
|
4466
4493
|
function createSequentialBackfillCoordinator(store, jobPlans, options) {
|
|
4467
|
-
const
|
|
4494
|
+
const completion = createBackfillCompletionTracker(store);
|
|
4468
4495
|
let activeRunner = null;
|
|
4469
4496
|
let activePlan = null;
|
|
4470
4497
|
let activePollTimer = null;
|
|
4471
4498
|
let nextJobIndex = 0;
|
|
4499
|
+
let startedAny = false;
|
|
4472
4500
|
let stopped = false;
|
|
4473
4501
|
const clearPollTimer = () => {
|
|
4474
4502
|
if (!activePollTimer) return;
|
|
@@ -4477,7 +4505,7 @@ function createSequentialBackfillCoordinator(store, jobPlans, options) {
|
|
|
4477
4505
|
};
|
|
4478
4506
|
const schedulePoll = (fn) => {
|
|
4479
4507
|
clearPollTimer();
|
|
4480
|
-
activePollTimer = setTimeout(fn,
|
|
4508
|
+
activePollTimer = setTimeout(fn, BACKFILL_POLL_INTERVAL_MS);
|
|
4481
4509
|
if (typeof activePollTimer === "object" && "unref" in activePollTimer) activePollTimer.unref();
|
|
4482
4510
|
};
|
|
4483
4511
|
const startNextJob = () => {
|
|
@@ -4487,18 +4515,20 @@ function createSequentialBackfillCoordinator(store, jobPlans, options) {
|
|
|
4487
4515
|
const plan = jobPlans[nextJobIndex++];
|
|
4488
4516
|
if (!plan) continue;
|
|
4489
4517
|
if (!plan.isPending(store.db)) continue;
|
|
4490
|
-
activePlan = plan;
|
|
4518
|
+
activePlan = completion.begin(plan);
|
|
4491
4519
|
activeRunner = plan.createRunner();
|
|
4520
|
+
startedAny = true;
|
|
4492
4521
|
options.logger.step(`${plan.name} backfill started`);
|
|
4493
4522
|
activeRunner.start();
|
|
4494
4523
|
schedulePoll(waitForCurrentJob);
|
|
4495
4524
|
return;
|
|
4496
4525
|
}
|
|
4497
|
-
options.logger.step("All backfill jobs complete");
|
|
4526
|
+
if (startedAny) options.logger.step("All backfill jobs complete");
|
|
4498
4527
|
};
|
|
4499
4528
|
const waitForCurrentJob = () => {
|
|
4500
4529
|
if (stopped || options.signal?.aborted || !activePlan || !activeRunner) return;
|
|
4501
|
-
|
|
4530
|
+
const job = getMaintenanceJob(store.db, activePlan.kind);
|
|
4531
|
+
if (job?.status === "failed") {
|
|
4502
4532
|
const failedPlan = activePlan;
|
|
4503
4533
|
const failedRunner = activeRunner;
|
|
4504
4534
|
activePlan = null;
|
|
@@ -4511,7 +4541,7 @@ function createSequentialBackfillCoordinator(store, jobPlans, options) {
|
|
|
4511
4541
|
});
|
|
4512
4542
|
return;
|
|
4513
4543
|
}
|
|
4514
|
-
if (
|
|
4544
|
+
if (completion.finished(activePlan, job)) {
|
|
4515
4545
|
const finishedPlan = activePlan;
|
|
4516
4546
|
const finishedRunner = activeRunner;
|
|
4517
4547
|
activePlan = null;
|
|
@@ -4528,10 +4558,6 @@ function createSequentialBackfillCoordinator(store, jobPlans, options) {
|
|
|
4528
4558
|
};
|
|
4529
4559
|
return {
|
|
4530
4560
|
start: () => {
|
|
4531
|
-
if (stopped || options.signal?.aborted) return;
|
|
4532
|
-
const pendingCount = jobPlans.filter((plan) => plan.isPending(store.db)).length;
|
|
4533
|
-
if (pendingCount === 0) return;
|
|
4534
|
-
options.logger.step(`${pendingCount} backfill job(s) pending — starting sequential runners`);
|
|
4535
4561
|
startNextJob();
|
|
4536
4562
|
},
|
|
4537
4563
|
stop: async () => {
|
|
@@ -11685,6 +11711,26 @@ function registerRootCommands(program) {
|
|
|
11685
11711
|
return program;
|
|
11686
11712
|
}
|
|
11687
11713
|
//#endregion
|
|
11714
|
+
//#region src/database-busy.ts
|
|
11715
|
+
var DATABASE_BUSY_MESSAGE = "The codemem database is busy: another codemem process is holding a write lock (for example, maintenance after an upgrade). Try again in a minute; `codemem maintenance status` shows running jobs.";
|
|
11716
|
+
function isDatabaseBusyError(error) {
|
|
11717
|
+
const code = error?.code;
|
|
11718
|
+
return code === "SQLITE_BUSY" || code === "SQLITE_LOCKED";
|
|
11719
|
+
}
|
|
11720
|
+
/**
|
|
11721
|
+
* Report a locked database with a plain message instead of a stack trace.
|
|
11722
|
+
* Other uncaught errors keep Node's default output and exit status.
|
|
11723
|
+
*/
|
|
11724
|
+
function installDatabaseBusyHandler(target = process) {
|
|
11725
|
+
const report = (error) => {
|
|
11726
|
+
let text = DATABASE_BUSY_MESSAGE;
|
|
11727
|
+
if (!isDatabaseBusyError(error)) text = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
11728
|
+
target.stderr.write(`${text}\n`, () => target.exit(1));
|
|
11729
|
+
};
|
|
11730
|
+
target.on("uncaughtException", report);
|
|
11731
|
+
target.on("unhandledRejection", report);
|
|
11732
|
+
}
|
|
11733
|
+
//#endregion
|
|
11688
11734
|
//#region src/index.ts
|
|
11689
11735
|
/**
|
|
11690
11736
|
* @codemem/cli — CLI entry point.
|
|
@@ -11728,6 +11774,7 @@ if (hasRootFlag("--cleanup-completion")) {
|
|
|
11728
11774
|
process.exit(0);
|
|
11729
11775
|
}
|
|
11730
11776
|
registerRootCommands(program);
|
|
11777
|
+
installDatabaseBusyHandler();
|
|
11731
11778
|
program.parse();
|
|
11732
11779
|
//#endregion
|
|
11733
11780
|
export {};
|