diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1440,21 +1440,28 @@ return fClean ? DisconnectResult::OK : DisconnectResult::UNCLEAN; } -static void FlushBlockFile(bool fFinalize = false) { - LOCK(cs_LastBlockFile); +static void FlushUndoFile(int block_file, bool finalize = false) { + FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize); + if (!UndoFileSeq().Flush(undo_pos_old, finalize)) { + AbortNode("Flushing undo file to disk failed. This is likely the " + "result of an I/O error."); + } +} +static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false) { + LOCK(cs_LastBlockFile); FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize); - FlatFilePos undo_pos_old(nLastBlockFile, - vinfoBlockFile[nLastBlockFile].nUndoSize); - - bool status = true; - status &= BlockFileSeq().Flush(block_pos_old, fFinalize); - status &= UndoFileSeq().Flush(undo_pos_old, fFinalize); - if (!status) { + if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) { AbortNode("Flushing block file to disk failed. This is likely the " "result of an I/O error."); } + // we do not always flush the undo file, as the chain tip may be lagging + // behind the incoming blocks, + // e.g. during IBD or a sync after a node going offline + if (!fFinalize || finalize_undo) { + FlushUndoFile(nLastBlockFile, finalize_undo); + } } static bool FindUndoPos(BlockValidationState &state, int nFile, @@ -1475,6 +1482,18 @@ chainparams.DiskMagic())) { return AbortNode(state, "Failed to write undo data"); } + // rev files are written in block height order, whereas blk files are + // written as blocks come in (often out of order) we want to flush the + // rev (undo) file once we've written the last block, which is indicated + // by the last height in the block file info as below; note that this + // does not catch the case where the undo writes are keeping up with the + // block writes (usually when a synced up node is getting newly mined + // blocks) -- this case is caught in the FindBlockPos function + if (_pos.nFile < nLastBlockFile && + static_cast(pindex->nHeight) == + vinfoBlockFile[_pos.nFile].nHeightLast) { + FlushUndoFile(_pos.nFile, true); + } // update nUndoPos in block index pindex->nUndoPos = _pos.nPos; @@ -3637,8 +3656,15 @@ vinfoBlockFile.resize(nFile + 1); } + bool finalize_undo = false; if (!fKnown) { while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + // when the undo file is keeping up with the block file, we want to + // flush it explicitly when it is lagging behind (more blocks arrive + // than are being connected), we let the undo block write case + // handle it + finalize_undo = (vinfoBlockFile[nFile].nHeightLast == + (unsigned int)ChainActive().Tip()->nHeight); nFile++; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); @@ -3653,7 +3679,7 @@ LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString()); } - FlushBlockFile(!fKnown); + FlushBlockFile(!fKnown, finalize_undo); nLastBlockFile = nFile; }