diff options
author | Pieter Wuille <pieter.wuille@gmail.com> | 2012-07-06 16:33:34 +0200 |
---|---|---|
committer | Pieter Wuille <pieter.wuille@gmail.com> | 2012-10-20 23:08:57 +0200 |
commit | ae8bfd12daa802d20791e69d3477e799d2b99f45 (patch) | |
tree | 848867135da5954b7155ebe98c09db66d09d5737 /src/main.cpp | |
parent | 450cbb0944cd20a06ce806e6679a1f4c83c50db2 (diff) |
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.
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 146 |
1 files changed, 59 insertions, 87 deletions
diff --git a/src/main.cpp b/src/main.cpp index ef5f627d94..c23aae3206 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -168,6 +168,7 @@ bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; } 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 @@ bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); } 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,18 +213,24 @@ bool CCoinsViewCache::SetBestBlock(CBlockIndex *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; +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() { + 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. It does not check for spendings by memory pool transactions. */ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } @@ -249,7 +250,7 @@ bool CCoinsViewMemPool::HaveCoins(uint256 txid) { return mempool.exists(txid) || base->HaveCoins(txid); } - +CCoinsViewCache *pcoinsTip = NULL; ////////////////////////////////////////////////////////////////////////////// // @@ -450,9 +451,8 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) 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 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) } } -bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, +bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) { if (pfMissingInputs) @@ -668,9 +668,7 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, 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 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, 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 @@ int CMerkleTx::GetBlocksToMaturity() const } -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() -{ - CCoinsDB coinsdb("r"); - return AcceptToMemoryPool(coinsdb); -} - -bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) +bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs) { - { LOCK(mempool.cs); // Add previous supporting transactions first @@ -882,20 +873,15 @@ bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs) 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 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock 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::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck 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 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) 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 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) 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 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) 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 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) } // 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 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) // 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 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) 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 @@ bool LoadBlockIndex(bool fAllowNew) // 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 @@ string GetWarnings(string strFor) // -bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv) +bool static AlreadyHave(const CInv& inv) { switch (inv.type) { @@ -2504,7 +2481,7 @@ bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv) 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 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) break; } } - CCoinsDB coinsdb("r"); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -2757,7 +2733,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 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 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; CDataStream vMsg(vRecv); - CCoinsDB coinsdb("r"); CTransaction tx; vRecv >> tx; @@ -2937,7 +2912,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 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 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) 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 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // 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 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) 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 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) 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"); } |