Skip to content

Commit f0207e0

Browse files
committed
blockstorage: Return on fatal block file flush error
By returning an error code if `FlushBlockFile` fails, the caller now has to explicitly handle block file flushing errors. Before this change such errors were non-explicitly ignored without a clear rationale. Prior to this patch `FlushBlockFile` may have failed silently in `Chainstate::FlushStateToDisk`. Improve this with a log line. Also add a TODO comment to flesh out whether returning early in the case of an error is appropriate or not. Returning early might be appropriate to prohibit `WriteBlockIndexDB` from writing a block index entry that does not refer to a fully flushed block. Besides `Chainstate::FlushStateToDisk`, `FlushBlockFile` is also called by `FindBlockPos`. Don't change the abort behavior there, since we don't want to fail the function if the flushing of already written blocks fails. Instead, just document it.
1 parent 5671c15 commit f0207e0

File tree

3 files changed

+27
-5
lines changed

3 files changed

+27
-5
lines changed

src/node/blockstorage.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -546,26 +546,29 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize)
546546
}
547547
}
548548

549-
void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
549+
bool BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
550550
{
551+
bool success = true;
551552
LOCK(cs_LastBlockFile);
552553

553554
if (m_blockfile_info.size() < 1) {
554555
// Return if we haven't loaded any blockfiles yet. This happens during
555556
// chainstate init, when we call ChainstateManager::MaybeRebalanceCaches() (which
556557
// then calls FlushStateToDisk()), resulting in a call to this function before we
557558
// have populated `m_blockfile_info` via LoadBlockIndexDB().
558-
return;
559+
return true;
559560
}
560561
assert(static_cast<int>(m_blockfile_info.size()) > m_last_blockfile);
561562

562563
FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize);
563564
if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
564565
m_opts.notifications.flushError("Flushing block file to disk failed. This is likely the result of an I/O error.");
566+
success = false;
565567
}
566568
// we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
567569
// e.g. during IBD or a sync after a node going offline
568570
if (!fFinalize || finalize_undo) FlushUndoFile(m_last_blockfile, finalize_undo);
571+
return success;
569572
}
570573

571574
uint64_t BlockManager::CalculateCurrentUsage()
@@ -658,7 +661,19 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne
658661
if (!fKnown) {
659662
LogPrint(BCLog::BLOCKSTORAGE, "Leaving block file %i: %s\n", m_last_blockfile, m_blockfile_info[m_last_blockfile].ToString());
660663
}
661-
FlushBlockFile(!fKnown, finalize_undo);
664+
665+
// Do not propagate the return code. The flush concerns a previous block
666+
// and undo file that has already been written to. If a flush fails
667+
// here, and we crash, there is no expected additional block data
668+
// inconsistency arising from the flush failure here. However, the undo
669+
// data may be inconsistent after a crash if the flush is called during
670+
// a reindex. A flush error might also leave some of the data files
671+
// untrimmed.
672+
if (!FlushBlockFile(!fKnown, finalize_undo)) {
673+
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning,
674+
"Failed to flush previous block file %05i (finalize=%i, finalize_undo=%i) before opening new block file %05i\n",
675+
m_last_blockfile, !fKnown, finalize_undo, nFile);
676+
}
662677
m_last_blockfile = nFile;
663678
m_undo_height_in_last_blockfile = 0; // No undo data yet in the new file, so reset our undo-height tracking.
664679
}

src/node/blockstorage.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ class BlockManager
9191
*/
9292
bool LoadBlockIndex()
9393
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
94-
void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
94+
95+
/** Return false if block file flushing fails. */
96+
[[nodiscard]] bool FlushBlockFile(bool fFinalize = false, bool finalize_undo = false);
97+
9598
void FlushUndoFile(int block_file, bool finalize = false);
9699
[[nodiscard]] bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown);
97100
bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);

src/validation.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -2511,7 +2511,11 @@ bool Chainstate::FlushStateToDisk(
25112511
LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);
25122512

25132513
// First make sure all block and undo data is flushed to disk.
2514-
m_blockman.FlushBlockFile();
2514+
// TODO: Handle return error, or add detailed comment why it is
2515+
// safe to not return an error upon failure.
2516+
if (!m_blockman.FlushBlockFile()) {
2517+
LogPrintLevel(BCLog::VALIDATION, BCLog::Level::Warning, "%s: Failed to flush block file.\n", __func__);
2518+
}
25152519
}
25162520

25172521
// Then update all block file information (which may refer to block and undo files).

0 commit comments

Comments
 (0)