--- id: "bug-1409" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "controller" severity: "hang" timestamp: "2026-07-04T17:00:00.000Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/controller/persist.c"] tags: ["raft-apply-decoupling", "task-6.1", "apply-pump", "busy-loop", "infinite-loop", "commit-resolve-pending", "regression-sweep"] related: [] legacy: true --- # bug-1409: test_demotion_convergence hung/spun (18.5M+ log lines, timeout kill) once commit.c's… ## Description test_demotion_convergence hung/spun (18.5M+ log lines, timeout kill) once commit.c's commit_resolve_pending unconditionally called the new persist_apply_pump_kick ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause apply_pump_drain()'s recheck-then-clear retry loop ignored persist_apply_pump_run()'s return code: on a genuine read failure (e.g. committed_lsn advanced in a test fixture with no real WAL bytes on disk for that LSN, rc=-ENOENT), last_applied_lsn never advances, so the very next re-check inside the SAME drain loop sees identical unresolved work and immediately reclaims the scheduled flag and retries forever -- a tight busy loop with no backoff, hammering disk reads and the logger. ## Fix apply_pump_drain() now checks persist_apply_pump_run()'s rc immediately after clearing apply_pump_scheduled; a negative rc returns immediately instead of re-checking for more work and reclaiming the flag. The failure is already logged loudly inside persist_apply_pump_run itself. A future, genuinely NEW kick (a later real commit-advance event) retries then, at that event's natural cadence, never in a busy spin. ## Verification _Not recorded (migrated from buglog.json)._ ## References _Not recorded (migrated from buglog.json)._