Skip to content

Commit 011fe00

Browse files
committed
Merge #17994: validation: flush undo files after last block write
ac94141 validation: delay flushing undo files in syncing node case (Karl-Johan Alm) Pull request description: Fixes #17890. Replaces #17892. Data files (`{blk|rev}<number>.dat`) pre-allocate space as they are written, and then trims down to the final size once they move on to the next sequence ("finalized flush"). The code currently assumes (incorrectly) that blk and rev files finish at the same time, but because blk files are written as blocks come in, and rev files are written in block height order, rev files end up being written to for awhile after moving on to the next block file, resulting in pre-allocation and waste of up to 1 MB of space per rev file. The exact point at which rev file writing finishes is the highest height block found inside the corresponding block file, which is already available in the CBlockFileInfo vector. This PR moves finalized flushing of undo files to to directly after the undo data for the previous block file has been written. There is a branch with annotation that demonstrates how this is handling flushing here: https://github.com/kallewoof/bitcoin/tree/200124-rev-files-annotated ACKs for top commit: vasild: ACK ac94141 (no changes in the code since ed34e00da). fjahr: Code review re-ACK ac94141 jonatack: Code review ACK ac94141 Tree-SHA512: 1d4e3b3d1d99bd7ebe7a2f632b1231146dd4f9f993c54db3a4090d9c086d95d2e4c327fd936066392b3afc6277b8f3a908d5c5993d4c8e49f72b92a417716dd2
2 parents b46fb5c + ac94141 commit 011fe00

File tree

1 file changed

+27
-9
lines changed

1 file changed

+27
-9
lines changed

src/validation.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,19 +1769,24 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
17691769
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
17701770
}
17711771

1772-
void static FlushBlockFile(bool fFinalize = false)
1772+
static void FlushUndoFile(int block_file, bool finalize = false)
17731773
{
1774-
LOCK(cs_LastBlockFile);
1774+
FlatFilePos undo_pos_old(block_file, vinfoBlockFile[block_file].nUndoSize);
1775+
if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
1776+
AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
1777+
}
1778+
}
17751779

1780+
static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false)
1781+
{
1782+
LOCK(cs_LastBlockFile);
17761783
FlatFilePos block_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nSize);
1777-
FlatFilePos undo_pos_old(nLastBlockFile, vinfoBlockFile[nLastBlockFile].nUndoSize);
1778-
1779-
bool status = true;
1780-
status &= BlockFileSeq().Flush(block_pos_old, fFinalize);
1781-
status &= UndoFileSeq().Flush(undo_pos_old, fFinalize);
1782-
if (!status) {
1784+
if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
17831785
AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
17841786
}
1787+
// we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
1788+
// e.g. during IBD or a sync after a node going offline
1789+
if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo);
17851790
}
17861791

17871792
static bool FindUndoPos(BlockValidationState &state, int nFile, FlatFilePos &pos, unsigned int nAddSize);
@@ -1795,6 +1800,14 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationSt
17951800
return error("ConnectBlock(): FindUndoPos failed");
17961801
if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
17971802
return AbortNode(state, "Failed to write undo data");
1803+
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
1804+
// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
1805+
// in the block file info as below; note that this does not catch the case where the undo writes are keeping up
1806+
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
1807+
// the FindBlockPos function
1808+
if (_pos.nFile < nLastBlockFile && static_cast<uint32_t>(pindex->nHeight) == vinfoBlockFile[_pos.nFile].nHeightLast) {
1809+
FlushUndoFile(_pos.nFile, true);
1810+
}
17981811

17991812
// update nUndoPos in block index
18001813
pindex->nUndoPos = _pos.nPos;
@@ -3244,8 +3257,13 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
32443257
vinfoBlockFile.resize(nFile + 1);
32453258
}
32463259

3260+
bool finalize_undo = false;
32473261
if (!fKnown) {
32483262
while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
3263+
// when the undo file is keeping up with the block file, we want to flush it explicitly
3264+
// when it is lagging behind (more blocks arrive than are being connected), we let the
3265+
// undo block write case handle it
3266+
finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)ChainActive().Tip()->nHeight);
32493267
nFile++;
32503268
if (vinfoBlockFile.size() <= nFile) {
32513269
vinfoBlockFile.resize(nFile + 1);
@@ -3259,7 +3277,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n
32593277
if (!fKnown) {
32603278
LogPrintf("Leaving block file %i: %s\n", nLastBlockFile, vinfoBlockFile[nLastBlockFile].ToString());
32613279
}
3262-
FlushBlockFile(!fKnown);
3280+
FlushBlockFile(!fKnown, finalize_undo);
32633281
nLastBlockFile = nFile;
32643282
}
32653283

0 commit comments

Comments
 (0)