Browse Source

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.
0.8
Pieter Wuille 13 years ago
parent
commit
d68dcf741e
  1. 72
      src/main.cpp
  2. 3
      src/main.h

72
src/main.cpp

@ -1457,26 +1457,18 @@ runCommand(std::string strCommand) @@ -1457,26 +1457,18 @@ runCommand(std::string strCommand)
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
}
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
{
assert(pindexNew->pprev == pindexBest);
uint256 hash = GetHash();
txdb.TxnBegin();
if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
{
txdb.WriteHashBestChain(hash);
if (!txdb.TxnCommit())
return error("SetBestChain() : TxnCommit failed");
pindexGenesisBlock = pindexNew;
}
else if (hashPrevBlock == hashBestChain)
{
// Adding to current best branch
if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
{
txdb.TxnAbort();
InvalidChainFound(pindexNew);
return error("SetBestChain() : ConnectBlock failed");
return false;
}
if (!txdb.TxnCommit())
return error("SetBestChain() : TxnCommit failed");
@ -1487,16 +1479,68 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) @@ -1487,16 +1479,68 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
// Delete redundant memory transactions
BOOST_FOREACH(CTransaction& tx, vtx)
tx.RemoveFromMemoryPool();
return true;
}
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
{
uint256 hash = GetHash();
txdb.TxnBegin();
if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
{
txdb.WriteHashBestChain(hash);
if (!txdb.TxnCommit())
return error("SetBestChain() : TxnCommit failed");
pindexGenesisBlock = pindexNew;
}
else if (hashPrevBlock == hashBestChain)
{
if (!SetBestChainInner(txdb, pindexNew))
return error("SetBestChain() : SetBestChainInner failed");
}
else
{
// New best branch
if (!Reorganize(txdb, pindexNew))
// the first block in the new chain that will cause it to become the new best chain
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();
InvalidChainFound(pindexNew);
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)

3
src/main.h

@ -1030,6 +1030,9 @@ public: @@ -1030,6 +1030,9 @@ public:
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
bool CheckBlock() const;
bool AcceptBlock();
private:
bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
};

Loading…
Cancel
Save