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 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 pollIntervalMs = 1e3;
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, pollIntervalMs);
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
- if (getMaintenanceJob(store.db, activePlan.kind)?.status === "failed") {
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 (!activePlan.isPending(store.db)) {
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 {};