()
| 258 | } |
| 259 | |
| 260 | private fire(): void { |
| 261 | this.log(`fire: wal=${this.mb(this.db.getWalSizeBytes())} baseline=${this.mb(this.sizeAtLastFullBackfill)}`); |
| 262 | const p = this.db |
| 263 | .checkpointWalPassive() |
| 264 | .then((res) => { |
| 265 | this.log(`timer pass: ${res ? `busy=${res.busy} log=${res.log} checkpointed=${res.checkpointed}` : 'null (machinery unavailable)'} wal=${this.mb(this.db.getWalSizeBytes())}`); |
| 266 | // Full backfill (busy 0, every log frame checkpointed) ⇒ the writer's |
| 267 | // next commit wraps the WAL; the file's current size becomes the new |
| 268 | // growth baseline. A partial pass (writer appended during it, or a |
| 269 | // read transaction pinned frames) leaves the baseline alone, so the |
| 270 | // next tick fires again and copies the remainder. In non-WAL mode |
| 271 | // SQLite reports log = checkpointed = -1, which is harmless here. |
| 272 | if (res && res.busy === 0 && res.log === res.checkpointed) { |
| 273 | this.sizeAtLastFullBackfill = this.db.getWalSizeBytes(); |
| 274 | // NO truncate here. A truncate checkpoint that starts against an |
| 275 | // ACTIVE writer wins the lock race and then blocks that writer for |
| 276 | // its entire backfill — after a multi-GB single-transaction burst |
| 277 | // (edge-index recreate) that exceeds the writer's 5s busy_timeout |
| 278 | // and fails the index with "database is locked" (§7a.2 record run). |
| 279 | // The file chop happens exclusively at parked barriers |
| 280 | // (backpressure/foldNow), where the writer is awaiting us by |
| 281 | // construction and cannot collide. |
| 282 | } |
| 283 | }) |
| 284 | .catch(() => { /* best-effort */ }) |
| 285 | .finally(() => { |
| 286 | if (this.inflight === p) this.inflight = null; |
| 287 | }); |
| 288 | this.inflight = p; |
| 289 | } |
| 290 | } |
no test coverage detected