فهرست منبع

fix(db): WAL truncate at parked barriers ONLY — the timer-path truncate loses the race it was assumed to lose (#1336)

A truncate checkpoint started against an ACTIVE writer wins the lock and
then blocks that writer for its entire backfill; after the edge-index
recreate's multi-GB single-transaction burst that exceeds the writer's 5s
busy_timeout and fails the index with 'database is locked' (§7a.2 record
run, EXIT=1 at kernel scale — the small mid-resolution truncates folded in
ms and masked the hazard). Barrier truncates (backpressure/foldNow) are
collision-free by construction: the writer is awaiting the valve.

Dubbo gate: exit 0, peak 81MB (barrier folds carry containment), dump
byte-identical. Valve + sizing suites 27/27.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Colby Mchenry 1 ماه پیش
والد
کامیت
2adc7f60c0
1فایلهای تغییر یافته به همراه8 افزوده شده و 10 حذف شده
  1. 8 10
      src/db/wal-valve.ts

+ 8 - 10
src/db/wal-valve.ts

@@ -263,16 +263,14 @@ export class WalCheckpointValve {
         // SQLite reports log = checkpointed = -1, which is harmless here.
         if (res && res.busy === 0 && res.log === res.checkpointed) {
           this.sizeAtLastFullBackfill = this.db.getWalSizeBytes();
-          // Opportunistic file chop while everything is folded: best-effort —
-          // an active writer/reader turns it into a busy no-op, and the
-          // barrier-path truncate (backpressure file cap) remains the
-          // deterministic bound.
-          if (this.db.getWalSizeBytes() > this.softBytes * 2) {
-            return this.db.checkpointWalTruncate().then((t) => {
-              if (t) this.log(`timer truncate: busy=${t.busy} wal=${this.mb(this.db.getWalSizeBytes())}`);
-              this.sizeAtLastFullBackfill = this.db.getWalSizeBytes();
-            });
-          }
+          // NO truncate here. A truncate checkpoint that starts against an
+          // ACTIVE writer wins the lock race and then blocks that writer for
+          // its entire backfill — after a multi-GB single-transaction burst
+          // (edge-index recreate) that exceeds the writer's 5s busy_timeout
+          // and fails the index with "database is locked" (§7a.2 record run).
+          // The file chop happens exclusively at parked barriers
+          // (backpressure/foldNow), where the writer is awaiting us by
+          // construction and cannot collide.
         }
       })
       .catch(() => { /* best-effort */ })