From 4c342fae290ed3d4dfbf3e024d9cabbb9fb8d31e Mon Sep 17 00:00:00 2001 From: Brannon King Date: Wed, 12 Feb 2020 11:40:14 -0700 Subject: [PATCH] flush more, disk sync less --- src/init.cpp | 4 ++-- src/txdb.cpp | 17 +++++++---------- src/validation.cpp | 33 +++++++++++++++------------------ src/validation.h | 2 +- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 12783153f..c401cf267 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -241,7 +241,7 @@ void Shutdown() // FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing if (pcoinsTip != nullptr) { - FlushStateToDisk(); + FlushStateToDisk(true); } // After there are no more peers/RPC left to give us new data which may generate @@ -257,7 +257,7 @@ void Shutdown() { LOCK(cs_main); if (pcoinsTip != nullptr) { - FlushStateToDisk(); + FlushStateToDisk(true); } pcoinsTip.reset(); pcoinscatcher.reset(); diff --git a/src/txdb.cpp b/src/txdb.cpp index 09e7ad83c..fc38c6ce3 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -115,7 +115,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?"; auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)"; - for (auto it = mapCoins.begin(); it != mapCoins.end();) { + for (auto it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) { if (it->second.flags & CCoinsCacheEntry::DIRTY) { if (it->second.coin.IsSpent()) { // at present the "IsSpent" flag is used for both "spent" and "block going backwards" @@ -135,10 +135,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo } changed++; } - count++; - auto itOld = it++; - mapCoins.erase(itOld); - if (crash_simulate && count % 200000 == 0) { + if (crash_simulate && ++count % 200000 == 0) { static FastRandomContext rng; if (rng.randrange(crash_simulate) == 0) { LogPrintf("Simulating a crash. Goodbye.\n"); @@ -153,14 +150,14 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo auto code = sqlite::commit(db); if (code != SQLITE_OK) { - LogPrint(BCLog::COINDB, "Error committing transaction outputs changes to coin database. SQLite error: %d\n", code); + LogPrintf("%s: Error committing coins info to database. SQLite error: %d\n", __func__, code); return false; } - LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); + LogPrint(BCLog::COINDB, "Committed %zu changed transaction outputs (out of %zu) to coin database...\n", changed, count); if (sync) { - auto code = sqlite::sync(db); + code = sqlite::sync(db); if (code != SQLITE_OK) { - LogPrint(BCLog::COINDB, "Error syncing coin database. SQLite error: %d\n", code); + LogPrintf("%s: Error syncing coin database. SQLite error: %d\n", __func__, code); return false; } } @@ -340,7 +337,7 @@ bool CBlockTreeDB::BatchWrite(const std::vector& setFilesToPrune, int nManualPruneHeight); static void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight); bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); @@ -2117,7 +2117,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl * If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything * besides checking if we need to prune. */ -bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { +bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, + FlushStateMode mode, int nManualPruneHeight, bool syncToDisk) { int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); LOCK(cs_main); static int64_t nLastWrite = 0; @@ -2186,7 +2187,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & vBlocks.push_back(*it); setDirtyBlockIndex.erase(it++); } - if (!pblocktree->BatchWrite(vFiles, nLastBlockFile, vBlocks, mode == FlushStateMode::ALWAYS)) { + if (!pblocktree->BatchWrite(vFiles, nLastBlockFile, vBlocks, syncToDisk)) { return AbortNode(state, "Failed to write to block index database"); } } @@ -2204,10 +2205,10 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & // overwrite one. Still, use a conservative safety factor of 2. if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) return state.Error("out of disk space"); - if (mode == FlushStateMode::ALWAYS && !pclaimTrie->SyncToDisk()) + if (syncToDisk && !pclaimTrie->SyncToDisk()) return state.Error("Failed to write to claim trie database"); // Flush the chainstate (which may refer to block index entries). - if (!pcoinsTip->Flush(mode == FlushStateMode::ALWAYS)) + if (!pcoinsTip->Flush(syncToDisk)) return AbortNode(state, "Failed to write to coin database"); nLastFlush = nNow; full_flush_completed = true; @@ -2224,10 +2225,10 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & return true; } -void FlushStateToDisk() { +void FlushStateToDisk(bool diskSync) { CValidationState state; const CChainParams& chainparams = Params(); - if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS, 0, diskSync)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); } } @@ -2355,7 +2356,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha } LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED)) + if (!IsInitialBlockDownload() && !FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) return false; if (disconnectpool) { @@ -2502,7 +2503,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); // Write the chain state to disk, if necessary. - if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED)) + if (!IsInitialBlockDownload() && !FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); @@ -2775,11 +2776,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& if (!blocks_connected) return true; const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip); - bool fInitialDownload = IsInitialBlockDownload(); // Notify external listeners about the new tip. // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected if (pindexFork != pindexNewTip) { + bool fInitialDownload = IsInitialBlockDownload(); // Notify ValidationInterface subscribers GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); @@ -2802,14 +2803,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& auto& consensus = chainparams.GetConsensus(); CheckBlockIndex(consensus); - auto flushMode = FlushStateMode::PERIODIC; - if (pindexNewTip && chainparams.NetworkIDString() != CBaseChainParams::REGTEST - && pindexNewTip->nTime + consensus.nPowTargetSpacing > GetAdjustedTime()) { - // trying to ensure that we flush to disk after new blocks when we're caught up to the chain - // they're technically allowed to be two hours late, but experience says one minute is more likely - flushMode = FlushStateMode::ALWAYS; - } - return FlushStateToDisk(chainparams, state, flushMode); + auto flushMode = IsInitialBlockDownload() ? FlushStateMode::IF_NEEDED : FlushStateMode::ALWAYS; + auto diskSync = chainparams.NetworkIDString() != CBaseChainParams::REGTEST + && flushMode == FlushStateMode::ALWAYS; + return FlushStateToDisk(chainparams, state, flushMode, 0, diskSync); } bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr pblock) { diff --git a/src/validation.h b/src/validation.h index f1c8017c5..835280de7 100644 --- a/src/validation.h +++ b/src/validation.h @@ -291,7 +291,7 @@ void PruneOneBlockFile(const int fileNumber); void UnlinkPrunedFiles(const std::set& setFilesToPrune); /** Flush all state, indexes and buffers to disk. */ -void FlushStateToDisk(); +void FlushStateToDisk(bool diskSync = false); /** Prune block files and flush state to disk. */ void PruneAndFlush(); /** Prune block files up to a given height */