Mercurial > hg > bitcoin
diff src/main.cpp @ 3650:12e3ba3a9c4b draft
Batch block connection during IBD
During the initial block download (or -loadblock), delay connection
of new blocks a bit, and perform them in a single action. This reduces
the load on the database engine, as subsequent blocks often update an
earlier block's transaction already.
author | Pieter Wuille <pieter.wuille@gmail.com> |
---|---|
date | Fri, 06 Jul 2012 16:33:34 +0200 (2012-07-06) |
parents | eb986f1e2e93 |
children | 17828f292f78 |
line wrap: on
line diff
--- a/src/main.cpp +++ b/src/main.cpp @@ -168,6 +168,7 @@ bool CCoinsView::HaveCoins(uint256 txid) { return false; } CBlockIndex *CCoinsView::GetBestBlock() { return NULL; } bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; } +bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; } CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { } bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); } @@ -176,13 +177,7 @@ CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); } bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } - -CCoinsViewDB::CCoinsViewDB(CCoinsDB &dbIn) : db(dbIn) {} -bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { return db.ReadCoins(txid, coins); } -bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { return db.WriteCoins(txid, coins); } -bool CCoinsViewDB::HaveCoins(uint256 txid) { return db.HaveCoins(txid); } -CBlockIndex *CCoinsViewDB::GetBestBlock() { return pindexBest; } -bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); } +bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); } CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { } @@ -218,16 +213,22 @@ return true; } +bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { + for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) + cacheCoins[it->first] = it->second; + pindexTip = pindex; + return true; +} + bool CCoinsViewCache::Flush() { - for (std::map<uint256,CCoins>::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) { - if (!base->SetCoins(it->first, it->second)) - return false; - } - if (!base->SetBestBlock(pindexTip)) - return false; - cacheCoins.clear(); - pindexTip = NULL; - return true; + bool fOk = base->BatchWrite(cacheCoins, pindexTip); + if (fOk) + cacheCoins.clear(); + return fOk; +} + +unsigned int CCoinsViewCache::GetCacheSize() { + return cacheCoins.size(); } /** CCoinsView that brings transactions from a memorypool into view. @@ -249,7 +250,7 @@ return mempool.exists(txid) || base->HaveCoins(txid); } - +CCoinsViewCache *pcoinsTip = NULL; ////////////////////////////////////////////////////////////////////////////// // @@ -450,9 +451,8 @@ CBlock blockTmp; if (pblock == NULL) { - CCoinsDB coinsdb("r"); CCoins coins; - if (coinsdb.ReadCoins(GetHash(), coins)) { + if (pcoinsTip->GetCoins(GetHash(), coins)) { CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); if (pindex) { if (!blockTmp.ReadFromDisk(pindex)) @@ -609,7 +609,7 @@ } } -bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, +bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) { if (pfMissingInputs) @@ -668,9 +668,7 @@ if (fCheckInputs) { - CCoinsViewDB viewDB(coinsdb); - CCoinsViewMemPool viewMemPool(viewDB, mempool); - CCoinsViewCache view(viewMemPool); + CCoinsViewCache &view = *pcoinsTip; // do we already have it? if (view.HaveCoins(hash)) @@ -758,9 +756,9 @@ return true; } -bool CTransaction::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs, bool* pfMissingInputs) +bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) { - return mempool.accept(coinsdb, *this, fCheckInputs, pfMissingInputs); + return mempool.accept(*this, fCheckInputs, pfMissingInputs); } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) @@ -849,31 +847,24 @@ } -bool CMerkleTx::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs) +bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs) { if (fClient) { if (!IsInMainChain() && !ClientCheckInputs()) return false; - return CTransaction::AcceptToMemoryPool(coinsdb, false); + return CTransaction::AcceptToMemoryPool(false); } else { - return CTransaction::AcceptToMemoryPool(coinsdb, fCheckInputs); + return CTransaction::AcceptToMemoryPool(fCheckInputs); } } -bool CMerkleTx::AcceptToMemoryPool() + + +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) { - CCoinsDB coinsdb("r"); - return AcceptToMemoryPool(coinsdb); -} - - - -bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) -{ - { LOCK(mempool.cs); // Add previous supporting transactions first @@ -882,20 +873,15 @@ if (!tx.IsCoinBase()) { uint256 hash = tx.GetHash(); - if (!mempool.exists(hash) && !coinsdb.HaveCoins(hash)) - tx.AcceptToMemoryPool(coinsdb, fCheckInputs); + if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash)) + tx.AcceptToMemoryPool(fCheckInputs); } } - return AcceptToMemoryPool(coinsdb, fCheckInputs); + return AcceptToMemoryPool(fCheckInputs); } return false; } -bool CWalletTx::AcceptWalletTransaction() -{ - CCoinsDB coinsdb("r"); - return AcceptWalletTransaction(coinsdb); -} // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) @@ -915,8 +901,7 @@ if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { - CCoinsDB coindb("r"); - CCoinsViewDB view(coindb); + CCoinsViewCache &view = *pcoinsTip; CCoins coins; if (view.GetCoins(hash, coins)) nHeight = coins.nHeight; @@ -1565,18 +1550,15 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) { - // if this functions exits prematurely, the transaction is aborted - CCoinsDB coinsdb; - if (!coinsdb.TxnBegin()) - return error("SetBestChain() : TxnBegin failed"); + CCoinsViewCache &view = *pcoinsTip; // special case for attaching the genesis block // note that no ConnectBlock is called, so its coinbase output is non-spendable if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock) { - coinsdb.WriteHashBestChain(pindexNew->GetBlockHash()); - if (!coinsdb.TxnCommit()) - return error("SetBestChain() : TxnCommit failed"); + view.SetBestBlock(pindexNew); + if (!view.Flush()) + return false; pindexGenesisBlock = pindexNew; pindexBest = pindexNew; hashBestChain = pindexNew->GetBlockHash(); @@ -1585,10 +1567,6 @@ return true; } - // create cached view to the coins database - CCoinsViewDB viewDB(coinsdb); - CCoinsViewCache view(viewDB); - // Find the fork (typically, there is none) CBlockIndex* pfork = view.GetBestBlock(); CBlockIndex* plonger = pindexNew; @@ -1625,8 +1603,11 @@ CBlock block; if (!block.ReadFromDisk(pindex)) return error("SetBestBlock() : ReadFromDisk for disconnect failed"); - if (!block.DisconnectBlock(pindex, view)) + CCoinsViewCache viewTemp(view, true); + if (!block.DisconnectBlock(pindex, viewTemp)) return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); + if (!viewTemp.Flush()) + return error("SetBestBlock() : Cache flush failed after disconnect"); // Queue memory transactions to resurrect BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -1646,10 +1627,13 @@ return error("SetBestBlock() : ReadFromDisk for connect failed"); pblock = █ } - if (!pblock->ConnectBlock(pindex, view)) { + CCoinsViewCache viewTemp(view, true); + if (!pblock->ConnectBlock(pindex, viewTemp)) { InvalidChainFound(pindexNew); return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); } + if (!viewTemp.Flush()) + return error("SetBestBlock() : Cache flush failed after connect"); // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, pblock->vtx) @@ -1657,11 +1641,10 @@ } // Make sure it's successfully written to disk before changing memory structure - if (!view.Flush()) - return error("SetBestBlock() : failed to write coin changes"); - if (!coinsdb.TxnCommit()) - return error("SetBestBlock() : TxnCommit failed"); - coinsdb.Close(); + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload || view.GetCacheSize()>5000) + if (!view.Flush()) + return false; // At this point, all changes have been done to the database. // Proceed by updating the memory structures. @@ -1678,14 +1661,13 @@ // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) - tx.AcceptToMemoryPool(coinsdb, false); + tx.AcceptToMemoryPool(false); // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) mempool.remove(tx); // Update best block in wallet (so we can detect restored wallets) - bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); @@ -1765,11 +1747,8 @@ return false; // New best - if (pindexNew->bnChainWork > bnBestChainWork) { - if (!IsInitialBlockDownload() || (pindexNew->nHeight % 1) == 0) - if (!SetBestChain(pindexNew)) - return false; - } + if (!SetBestChain(pindexNew)) + return false; if (pindexNew == pindexBest) { @@ -2169,11 +2148,9 @@ // Load block index // CChainDB chaindb("cr"); - CCoinsDB coinsdb("cr"); - if (!LoadBlockIndex(coinsdb, chaindb)) + if (!LoadBlockIndex(chaindb)) return false; chaindb.Close(); - coinsdb.Close(); // // Init with genesis block @@ -2492,7 +2469,7 @@ // -bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv) +bool static AlreadyHave(const CInv& inv) { switch (inv.type) { @@ -2504,7 +2481,7 @@ txInMap = mempool.exists(inv.hash); } return txInMap || mapOrphanTransactions.count(inv.hash) || - coinsdb.HaveCoins(inv.hash); + pcoinsTip->HaveCoins(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || @@ -2748,7 +2725,6 @@ break; } } - CCoinsDB coinsdb("r"); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -2757,7 +2733,7 @@ return true; pfrom->AddInventoryKnown(inv); - bool fAlreadyHave = AlreadyHave(coinsdb, inv); + bool fAlreadyHave = AlreadyHave(inv); if (fDebug) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); @@ -2929,7 +2905,6 @@ vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; CDataStream vMsg(vRecv); - CCoinsDB coinsdb("r"); CTransaction tx; vRecv >> tx; @@ -2937,7 +2912,7 @@ pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; - if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs)) + if (tx.AcceptToMemoryPool(true, &fMissingInputs)) { SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); @@ -2959,7 +2934,7 @@ CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; - if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs2)) + if (tx.AcceptToMemoryPool(true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); @@ -3407,11 +3382,10 @@ // vector<CInv> vGetData; int64 nNow = GetTime() * 1000000; - CCoinsDB coinsdb("r"); while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; - if (!AlreadyHave(coinsdb, inv)) + if (!AlreadyHave(inv)) { if (fDebugNet) printf("sending getdata: %s\n", inv.ToString().c_str()); @@ -3621,9 +3595,7 @@ int64 nFees = 0; { LOCK2(cs_main, mempool.cs); - CCoinsDB coinsdb("r"); - CCoinsViewDB viewdb(coinsdb); - CCoinsViewCache view(viewdb); + CCoinsViewCache view(*pcoinsTip, true); // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move @@ -3811,7 +3783,7 @@ CBlockIndex indexDummy(*pblock); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; - CCoinsViewCache viewNew(viewdb); + CCoinsViewCache viewNew(*pcoinsTip, true); if (!pblock->ConnectBlock(&indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); }