#36049 streams: avoid termination on buffered write failure
| Reviewability | Reviews | Agreement | Size | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| #36049 streams: avoid termination on buffered write failure | Waiting on author | 1 | Strong | 19 + 36 tests | ||||||
|
|
|
|
| ||||||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | ||||||||||
Goal
Clear pending buffer bytes before invoking the underlying stream write in `BufferedWriter::flush()` to avoid re-attempting writes during exception unwinding. Explicitly call `flush()` within try-catch blocks in `BlockManager::WriteBlock` and `BlockManager::WriteBlockUndo` so disk write errors trigger clean fatal error handling instead of aborting the process.
Problem: When block or undo writes fail due to disk issues (such as a full filesystem), `AutoFile::write_buffer()` throws an exception. Because `BufferedWriter` previously flushed pending bytes in its implicitly `noexcept` destructor, write failures invoked `std::terminate` instead of normal node storage error handling, and a failed explicit `flush()` would re-attempt the write upon destructor unwinding.
Evidence of importance:
- Author report: 'my 1 TB benchmarking servers have recently been filling up, so I've been seeing these failures more often.'
- PR description provides a minimal reproduction script demonstrating that short writes previously resulted in process aborts rather than a clean exit with 'Failed to write genesis block'.
- The bug was introduced in merged PR #31551, which added `BufferedWriter`.
Risk of a mistake: medium (validation). A flaw in block and undo write flush handling could lead to silent data loss or premature fatal errors during block storage.
Changed since the previous assessment: No substantial changes to the code or approach; commits now credit a co-author and refine unit test coverage.
Reviewability
Paused · Waiting on author
Open objection from maflcko with no author reply for 38 days; review would repeat what has been asked.
Waiting on (from the thread read):
- author: response to maflcko's suggestion regarding wholesale handling of exceptions during destruction 2026-08-21
- author: response to maflcko's suggestion on wholesale prevention of destructor exceptions 2026-08-21
Agreement
Strong
substantive support, no open objection (w0xlt)
Thread notes: Reclassified maflcko's comment from an objection to a suggestion since review-time concern is not a harm of merging, and added andrewtoth's answered question. / Added andrewtoth's answered question regarding termination vs exception handling. Reclassified maflcko's comment from an objection to a suggestion because inefficient use of review time is not a merging harm.
Objections, suggestions, and questions:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| andrewtoth (member) | question, approach | why storage write errors should be handled rather than immediately terminating the process | answered · author explained that handling the exception allows reporting the error and clean shutdown rather than std::terminate | answered | yes | 2026-08-20: 'Can you expand on why this is a problem? If there is a storage error, don't we have to crash anyways?' Status from: 2026-08-20: l0rinc: 'Handling the exception lets the node report the storage error and run its normal shutdown cleanup.' |
| maflcko (member) | suggestion, approach | find a code pattern or compiler/linter check to avoid exception-during-destruction issues wholesale rather than addressing them piecemeal | open | answered | no | 2026-08-21: 'It would be good to find a code pattern that avoids this class of problem wholesale. It doesn't seem a great use of human (or LLM) review time to spend on such issues one-by-one.' |
Support:
- w0xlt (contributor) (Concept ACK, 2026-09-05) [read: AutoFile, BufferedWriter]: encountered the same bug while working in this area and implemented an alternative fix to handle buffered write errors
- w0xlt (contributor) (ACK, 2026-09-24): (no reason given) [not substantive]
Participants:
- andrewtoth (member, question, 1 statements 2026-08-20 to 2026-08-20): asked why catching storage write errors is preferable to terminating immediately
- maflcko (member, neutral, 1 statements 2026-08-21 to 2026-08-21): suggested addressing exception-during-destruction issues wholesale rather than piecemeal
- w0xlt (contributor, support, 2 statements 2026-09-05 to 2026-09-24): ACKed cac9501; previously Concept ACKed after encountering the same bug
Files
36 lines under test/bench/ci.
- src/test/streams_tests.cpp +36/-0
- src/node/blockstorage.cpp +13/-3
- src/streams.h +6/-2
Card
The short text the ranking pass compares across PRs.
Prevents bitcoind from abruptly crashing via std::terminate when block or undo writes fail due to disk storage errors such as a full disk. BlockManager now explicitly flushes buffered writers within try-catch blocks to route errors to standard shutdown procedures, and BufferedWriter clears pending buffer offsets before writing to avoid re-triggering writes during stack unwinding. The author observed repeated crashes on 1 TB benchmark servers running out of space and provided a reproducer demonstrating that write failures now exit cleanly.
Data
thread read through 2026-09-24 (6 statements), head cac9501d35
labels: none
Categories
Utilities (logging, arguments, libraries)
P1 · bug fix (assessed alone as P2; Raised to P1: an uncatchable std::terminate on buffered write failure is a reliability/data-safety failure observed in practice, not a routine bug fix.)
Prevents `std::terminate` during stream destruction on write failures by ensuring `BufferedWriter` handles flush errors safely. The stream primitive's implicit `noexcept` destructor was terminating the process rather than allowing callers to handle write exceptions cleanly.
Membership: Modifies `BufferedWriter` in `src/streams.h` to clear pending buffer bytes before writing, avoiding destructor aborts during exception unwinding.
Factors: security/stability 2, bug 2, performance 0, user value 1, leverage 0
Validation
P2 · bug fix (ranking pass: Moved up: author observed real std::terminate crashes on disk-full; most certain benefit of the error-handling group)
Ensures disk write failures in block storage trigger clean fatal error handling rather than an abrupt process abort via `std::terminate`. As shown in the reproduction script, short writes to block files previously crashed the process instead of cleanly reporting storage errors.
Membership: Changes `src/node/blockstorage.cpp` error handling during `BlockManager::WriteBlock` and `BlockManager::WriteBlockUndo`.
Factors: security/stability 2, bug 2, performance 0, user value 1, leverage 0