0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-07 10:27:47 -05:00

validation: Make ReplayBlocks interruptible

Now, when an interrupt is received during ReplayBlocks the
intermediate progress is flushed:
In addition to updating the coins on disk, this
flush adjusts the head blocks, so that with the next
restart, the replay continues from where we stopped.
This commit is contained in:
Martin Zumsande 2024-05-17 17:49:20 -04:00
parent 6f36624147
commit 26e78f2ee4
2 changed files with 31 additions and 9 deletions

View file

@ -93,17 +93,21 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
size_t count = 0;
size_t changed = 0;
assert(!hashBlock.IsNull());
bool unfinished_replay{false};
uint256 old_tip = GetBestBlock();
if (old_tip.IsNull()) {
// We may be in the middle of replaying.
// We may be in the process of replaying.
std::vector<uint256> old_heads = GetHeadBlocks();
if (old_heads.size() == 2) {
if (old_heads[0] != hashBlock) {
LogPrintLevel(BCLog::COINDB, BCLog::Level::Error, "The coins database detected an inconsistent state, likely due to a previous crash or shutdown. You will need to restart bitcoind with the -reindex-chainstate or -reindex configuration option.\n");
// We haven't reached the target of replaying yet.
// This happens if the replay was interrupted by the user.
unfinished_replay = true;
} else {
// Replaying has reached its end, flush the results.
old_tip = old_heads[1];
}
assert(old_heads[0] == hashBlock);
old_tip = old_heads[1];
}
}
@ -111,8 +115,10 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
// transition from old_tip to hashBlock.
// A vector is used for future extensibility, as we may want to support
// interrupting after partial writes from multiple independent reorgs.
batch.Erase(DB_BEST_BLOCK);
batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
if (!unfinished_replay) {
batch.Erase(DB_BEST_BLOCK);
batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
}
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
@ -139,9 +145,19 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
}
}
// In the last batch, mark the database as consistent with hashBlock again.
batch.Erase(DB_HEAD_BLOCKS);
batch.Write(DB_BEST_BLOCK, hashBlock);
if (unfinished_replay) {
// This is called when, during ReplayBlocks, an interrupt is received.
// Update the replay origin (saved in second place in DB_HEAD_BLOCKS)
// so that the replay can pick up from here after restart.
// The replay target remains unchanged.
std::vector<uint256> old_heads = GetHeadBlocks();
assert(old_heads.size() == 2);
batch.Write(DB_HEAD_BLOCKS, Vector(old_heads[0], hashBlock));
} else {
// In the last batch, mark the database as consistent with hashBlock again.
batch.Erase(DB_HEAD_BLOCKS);
batch.Write(DB_BEST_BLOCK, hashBlock);
}
LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
bool ret = m_db->WriteBatch(batch);

View file

@ -4754,6 +4754,12 @@ bool Chainstate::ReplayBlocks()
LogPrintf("Rolling forward %s (%i)\n", pindex.GetBlockHash().ToString(), nHeight);
m_chainman.GetNotifications().progress(_("Replaying blocks…"), (int)((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)), false);
if (!RollforwardBlock(&pindex, cache)) return false;
if (m_chainman.m_interrupt) {
LogInfo("Flushing intermediate state of replay\n");
cache.SetBestBlock(pindex.GetBlockHash());
cache.Flush();
return false;
}
}
cache.SetBestBlock(pindexNew->GetBlockHash());