* With the writer parked on the returned promise, loop passive passes until * one reports the entire WAL backfilled (typically the second: the first * drains the pass that was already running against a stale snapshot). Gives * up after a bounded number of passes — e.g. a reader pinning the
()
| 224 | * because unbounded WAL growth degrades; a wedged writer never recovers. |
| 225 | */ |
| 226 | private async backfillFully(): Promise<void> { |
| 227 | for (let i = 0; i < MAX_PAUSED_BACKFILL_PASSES; i++) { |
| 228 | if (this.inflight) await this.inflight; // fold in the stale in-flight pass first |
| 229 | const res = await this.db.checkpointWalPassive(); |
| 230 | if (!res) return; // checkpoint machinery unavailable — don't spin |
| 231 | this.log(`backfill pass ${i + 1}: busy=${res.busy} log=${res.log} checkpointed=${res.checkpointed} wal=${this.mb(this.db.getWalSizeBytes())}`); |
| 232 | if (res.busy === 0 && res.log === res.checkpointed) { |
| 233 | // Backfill complete AND we are at a parked barrier (backfillFully only |
| 234 | // runs under a writer pause): the no-reader window is guaranteed, so |
| 235 | // chop the FILE too — a fully-backfilled WAL otherwise keeps growing |
| 236 | // whenever commits land while pool readers hold marks (§7a.1: 22GB |
| 237 | // on-disk at kernel scale despite backfills). A racing reader turns |
| 238 | // this into a no-op (busy=1); the passive result above still stands. |
| 239 | const trunc = await this.db.checkpointWalTruncate(); |
| 240 | if (trunc) this.log(`truncate: busy=${trunc.busy} wal=${this.mb(this.db.getWalSizeBytes())}`); |
| 241 | this.sizeAtLastFullBackfill = this.db.getWalSizeBytes(); |
| 242 | this.consecutiveGiveUps = 0; |
| 243 | this.futileUntil = 0; |
| 244 | return; |
| 245 | } |
| 246 | } |
| 247 | this.consecutiveGiveUps++; |
| 248 | if (this.consecutiveGiveUps >= 2) { |
| 249 | this.futileUntil = Date.now() + 60_000; |
| 250 | } |
| 251 | const msg = `backfill gave up after ${MAX_PAUSED_BACKFILL_PASSES} passes (streak ${this.consecutiveGiveUps}${this.futileUntil ? ', parking disabled 60s' : ''}) — a reader is pinning the WAL`; |
| 252 | this.log(msg); |
| 253 | // Give-ups are rare and load-bearing for §7a.1-class diagnosis — surface |
| 254 | // them on any timing-instrumented run, not just valve-debug ones. |
| 255 | if (process.env.CODEGRAPH_SYNTH_TIMINGS && !process.env.CODEGRAPH_WAL_VALVE_DEBUG) { |
| 256 | console.error(`[wal-valve] ${msg}`); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | private fire(): void { |
| 261 | this.log(`fire: wal=${this.mb(this.db.getWalSizeBytes())} baseline=${this.mb(this.sizeAtLastFullBackfill)}`); |
no test coverage detected