Limit the impact of reorganisations on the database
Sometimes a new block arrives in a new chain that was already the best valid one, but wasn't marked that way. This happens for example when network rules change to recover after a fork. In this case, it is not necessary to do the entire reorganisation inside a single db commit. These can become huge, and exceed the objects/lockers limits in bdb. This patch limits the blocks the actual reorganisation is applied to, and adds the next blocks afterwards in separate db transactions.
This commit is contained in:
parent
336ba312a6
commit
d68dcf741e
80
src/main.cpp
80
src/main.cpp
@ -1457,6 +1457,32 @@ runCommand(std::string strCommand)
|
|||||||
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
|
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
|
||||||
|
{
|
||||||
|
assert(pindexNew->pprev == pindexBest);
|
||||||
|
|
||||||
|
uint256 hash = GetHash();
|
||||||
|
|
||||||
|
// Adding to current best branch
|
||||||
|
if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
|
||||||
|
{
|
||||||
|
txdb.TxnAbort();
|
||||||
|
InvalidChainFound(pindexNew);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!txdb.TxnCommit())
|
||||||
|
return error("SetBestChain() : TxnCommit failed");
|
||||||
|
|
||||||
|
// Add to current best branch
|
||||||
|
pindexNew->pprev->pnext = pindexNew;
|
||||||
|
|
||||||
|
// Delete redundant memory transactions
|
||||||
|
BOOST_FOREACH(CTransaction& tx, vtx)
|
||||||
|
tx.RemoveFromMemoryPool();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
|
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
|
||||||
{
|
{
|
||||||
uint256 hash = GetHash();
|
uint256 hash = GetHash();
|
||||||
@ -1471,32 +1497,50 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
|
|||||||
}
|
}
|
||||||
else if (hashPrevBlock == hashBestChain)
|
else if (hashPrevBlock == hashBestChain)
|
||||||
{
|
{
|
||||||
// Adding to current best branch
|
if (!SetBestChainInner(txdb, pindexNew))
|
||||||
if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
|
return error("SetBestChain() : SetBestChainInner failed");
|
||||||
{
|
|
||||||
txdb.TxnAbort();
|
|
||||||
InvalidChainFound(pindexNew);
|
|
||||||
return error("SetBestChain() : ConnectBlock failed");
|
|
||||||
}
|
|
||||||
if (!txdb.TxnCommit())
|
|
||||||
return error("SetBestChain() : TxnCommit failed");
|
|
||||||
|
|
||||||
// Add to current best branch
|
|
||||||
pindexNew->pprev->pnext = pindexNew;
|
|
||||||
|
|
||||||
// Delete redundant memory transactions
|
|
||||||
BOOST_FOREACH(CTransaction& tx, vtx)
|
|
||||||
tx.RemoveFromMemoryPool();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// New best branch
|
// the first block in the new chain that will cause it to become the new best chain
|
||||||
if (!Reorganize(txdb, pindexNew))
|
CBlockIndex *pindexIntermediate = pindexNew;
|
||||||
|
|
||||||
|
// list of blocks that need to be connected afterwards
|
||||||
|
std::vector<CBlockIndex*> vpindexSecondary;
|
||||||
|
|
||||||
|
// Reorganize is costly in terms of db load, as it works in a single db transaction.
|
||||||
|
// Try to limit how much needs to be done inside
|
||||||
|
while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
|
||||||
|
{
|
||||||
|
vpindexSecondary.push_back(pindexIntermediate);
|
||||||
|
pindexIntermediate = pindexIntermediate->pprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vpindexSecondary.empty())
|
||||||
|
printf("Postponing %i reconnects\n", vpindexSecondary.size());
|
||||||
|
|
||||||
|
// Switch to new best branch
|
||||||
|
if (!Reorganize(txdb, pindexIntermediate))
|
||||||
{
|
{
|
||||||
txdb.TxnAbort();
|
txdb.TxnAbort();
|
||||||
InvalidChainFound(pindexNew);
|
InvalidChainFound(pindexNew);
|
||||||
return error("SetBestChain() : Reorganize failed");
|
return error("SetBestChain() : Reorganize failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect futher blocks
|
||||||
|
BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
|
||||||
|
{
|
||||||
|
CBlock block;
|
||||||
|
if (!block.ReadFromDisk(pindex))
|
||||||
|
{
|
||||||
|
printf("SetBestChain() : ReadFromDisk failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
txdb.TxnBegin();
|
||||||
|
// errors now are not fatal, we still did a reorganisation to a new chain in a valid way
|
||||||
|
if (!block.SetBestChainInner(txdb, pindex))
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update best block in wallet (so we can detect restored wallets)
|
// Update best block in wallet (so we can detect restored wallets)
|
||||||
|
@ -1030,6 +1030,9 @@ public:
|
|||||||
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
|
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
|
||||||
bool CheckBlock() const;
|
bool CheckBlock() const;
|
||||||
bool AcceptBlock();
|
bool AcceptBlock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user