@@ -130,6 +130,12 @@ namespace {
130130
131131 // Number of preferrable block download peers.
132132 int nPreferredDownload = 0 ;
133+
134+ // Dirty block index entries.
135+ set<CBlockIndex*> setDirtyBlockIndex;
136+
137+ // Dirty block file entries.
138+ set<int > setDirtyFileInfo;
133139} // anon namespace
134140
135141// ////////////////////////////////////////////////////////////////////////////
@@ -1137,11 +1143,6 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
11371143 pos.nPos = (unsigned int )fileOutPos;
11381144 fileout << block;
11391145
1140- // Flush stdio buffers and commit to disk before returning
1141- fflush (fileout.Get ());
1142- if (!IsInitialBlockDownload ())
1143- FileCommit (fileout.Get ());
1144-
11451146 return true ;
11461147}
11471148
@@ -1335,7 +1336,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
13351336 }
13361337 if (!state.CorruptionPossible ()) {
13371338 pindex->nStatus |= BLOCK_FAILED_VALID;
1338- pblocktree-> WriteBlockIndex ( CDiskBlockIndex ( pindex) );
1339+ setDirtyBlockIndex. insert ( pindex);
13391340 setBlockIndexCandidates.erase (pindex);
13401341 InvalidChainFound (pindex);
13411342 }
@@ -1732,10 +1733,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
17321733 }
17331734
17341735 pindex->RaiseValidity (BLOCK_VALID_SCRIPTS);
1735-
1736- CDiskBlockIndex blockindex (pindex);
1737- if (!pblocktree->WriteBlockIndex (blockindex))
1738- return state.Abort (" Failed to write block index" );
1736+ setDirtyBlockIndex.insert (pindex);
17391737 }
17401738
17411739 if (fTxIndex )
@@ -1759,26 +1757,61 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
17591757 return true ;
17601758}
17611759
1762- // Update the on-disk chain state.
1763- bool static WriteChainState (CValidationState &state, bool forceWrite=false ) {
1760+ /* *
1761+ * Update the on-disk chain state.
1762+ * The caches and indexes are flushed if either they're too large, forceWrite is set, or
1763+ * fast is not set and it's been a while since the last write.
1764+ */
1765+ bool static FlushStateToDisk (CValidationState &state, bool fast = false , bool forceWrite = false ) {
1766+ LOCK (cs_main);
17641767 static int64_t nLastWrite = 0 ;
1765- if (forceWrite || pcoinsTip->GetCacheSize () > nCoinCacheSize || (!IsInitialBlockDownload () && GetTimeMicros () > nLastWrite + 600 *1000000 )) {
1768+ if (forceWrite || pcoinsTip->GetCacheSize () > nCoinCacheSize ||
1769+ (!fast && GetTimeMicros () > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000 )) {
17661770 // Typical CCoins structures on disk are around 100 bytes in size.
17671771 // Pushing a new one to the database can cause it to be written
17681772 // twice (once in the log, and once in the tables). This is already
17691773 // an overestimation, as most will delete an existing entry or
17701774 // overwrite one. Still, use a conservative safety factor of 2.
17711775 if (!CheckDiskSpace (100 * 2 * 2 * pcoinsTip->GetCacheSize ()))
17721776 return state.Error (" out of disk space" );
1777+ // First make sure all block and undo data is flushed to disk.
17731778 FlushBlockFile ();
1779+ // Then update all block file information (which may refer to block and undo files).
1780+ bool fileschanged = false ;
1781+ for (set<int >::iterator it = setDirtyFileInfo.begin (); it != setDirtyFileInfo.end (); ) {
1782+ if (!pblocktree->WriteBlockFileInfo (*it, vinfoBlockFile[*it])) {
1783+ return state.Abort (" Failed to write to block index" );
1784+ }
1785+ fileschanged = true ;
1786+ setDirtyFileInfo.erase (it++);
1787+ }
1788+ if (fileschanged && !pblocktree->WriteLastBlockFile (nLastBlockFile)) {
1789+ return state.Abort (" Failed to write to block index" );
1790+ }
1791+ for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin (); it != setDirtyBlockIndex.end (); ) {
1792+ if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (*it))) {
1793+ return state.Abort (" Failed to write to block index" );
1794+ }
1795+ setDirtyBlockIndex.erase (it++);
1796+ }
17741797 pblocktree->Sync ();
1798+ // Finally flush the chainstate (which may refer to block index entries).
17751799 if (!pcoinsTip->Flush ())
17761800 return state.Abort (" Failed to write to coin database" );
1801+ // Update best block in wallet (so we can detect restored wallets).
1802+ if (forceWrite || !fast) {
1803+ g_signals.SetBestChain (chainActive.GetLocator ());
1804+ }
17771805 nLastWrite = GetTimeMicros ();
17781806 }
17791807 return true ;
17801808}
17811809
1810+ void FlushStateToDisk () {
1811+ CValidationState state;
1812+ FlushStateToDisk (state, false , true );
1813+ }
1814+
17821815// Update chainActive and related internal data structures.
17831816void static UpdateTip (CBlockIndex *pindexNew) {
17841817 chainActive.SetTip (pindexNew);
@@ -1837,7 +1870,7 @@ bool static DisconnectTip(CValidationState &state) {
18371870 }
18381871 LogPrint (" bench" , " - Disconnect block: %.2fms\n " , (GetTimeMicros () - nStart) * 0.001 );
18391872 // Write the chain state to disk, if necessary.
1840- if (!WriteChainState (state))
1873+ if (!FlushStateToDisk (state, true ))
18411874 return false ;
18421875 // Resurrect mempool transactions from the disconnected block.
18431876 BOOST_FOREACH (const CTransaction &tx, block.vtx ) {
@@ -1900,7 +1933,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
19001933 int64_t nTime4 = GetTimeMicros (); nTimeFlush += nTime4 - nTime3;
19011934 LogPrint (" bench" , " - Flush: %.2fms [%.2fs]\n " , (nTime4 - nTime3) * 0.001 , nTimeFlush * 0.000001 );
19021935 // Write the chain state to disk, if necessary.
1903- if (!WriteChainState (state))
1936+ if (!FlushStateToDisk (state, true ))
19041937 return false ;
19051938 int64_t nTime5 = GetTimeMicros (); nTimeChainState += nTime5 - nTime4;
19061939 LogPrint (" bench" , " - Writing chainstate: %.2fms [%.2fs]\n " , (nTime5 - nTime4) * 0.001 , nTimeChainState * 0.000001 );
@@ -1919,10 +1952,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
19191952 BOOST_FOREACH (const CTransaction &tx, pblock->vtx ) {
19201953 SyncWithWallets (tx, pblock);
19211954 }
1922- // Update best block in wallet (so we can detect restored wallets)
1923- // Emit this signal after the SyncWithWallets signals as the wallet relies on that everything up to this point has been synced
1924- if ((chainActive.Height () % 20160 ) == 0 || ((chainActive.Height () % 144 ) == 0 && !IsInitialBlockDownload ()))
1925- g_signals.SetBestChain (chainActive.GetLocator ());
19261955
19271956 int64_t nTime6 = GetTimeMicros (); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
19281957 LogPrint (" bench" , " - Connect postprocess: %.2fms [%.2fs]\n " , (nTime6 - nTime5) * 0.001 , nTimePostConnect * 0.000001 );
@@ -2043,9 +2072,6 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
20432072 else
20442073 CheckForkWarningConditions ();
20452074
2046- if (!pblocktree->Flush ())
2047- return state.Abort (" Failed to sync block index" );
2048-
20492075 return true ;
20502076}
20512077
@@ -2086,11 +2112,16 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
20862112 if (chainActive.Height () > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
20872113 pnode->PushInventory (CInv (MSG_BLOCK, hashNewTip));
20882114 }
2089-
2115+ // Notify external listeners about the new tip.
20902116 uiInterface.NotifyBlockTip (hashNewTip);
20912117 }
20922118 } while (pindexMostWork != chainActive.Tip ());
20932119
2120+ // Write changes periodically to disk, after relay.
2121+ if (!FlushStateToDisk (state)) {
2122+ return false ;
2123+ }
2124+
20942125 return true ;
20952126}
20962127
@@ -2123,8 +2154,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
21232154 if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork )
21242155 pindexBestHeader = pindexNew;
21252156
2126- // Ok if it fails, we'll download the header again next time.
2127- pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew));
2157+ setDirtyBlockIndex.insert (pindexNew);
21282158
21292159 return pindexNew;
21302160}
@@ -2143,6 +2173,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
21432173 LOCK (cs_nBlockSequenceId);
21442174 pindexNew->nSequenceId = nBlockSequenceId++;
21452175 }
2176+ setDirtyBlockIndex.insert (pindexNew);
21462177
21472178 if (pindexNew->pprev == NULL || pindexNew->pprev ->nChainTx ) {
21482179 // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
@@ -2162,24 +2193,18 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
21622193 range.first ++;
21632194 mapBlocksUnlinked.erase (it);
21642195 }
2165- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindex)))
2166- return state.Abort (" Failed to write block index" );
21672196 }
21682197 } else {
21692198 if (pindexNew->pprev && pindexNew->pprev ->IsValid (BLOCK_VALID_TREE)) {
21702199 mapBlocksUnlinked.insert (std::make_pair (pindexNew->pprev , pindexNew));
21712200 }
2172- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew)))
2173- return state.Abort (" Failed to write block index" );
21742201 }
21752202
21762203 return true ;
21772204}
21782205
21792206bool FindBlockPos (CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false )
21802207{
2181- bool fUpdatedLast = false ;
2182-
21832208 LOCK (cs_LastBlockFile);
21842209
21852210 unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
@@ -2195,7 +2220,6 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
21952220 if (vinfoBlockFile.size () <= nFile) {
21962221 vinfoBlockFile.resize (nFile + 1 );
21972222 }
2198- fUpdatedLast = true ;
21992223 }
22002224 pos.nFile = nFile;
22012225 pos.nPos = vinfoBlockFile[nFile].nSize ;
@@ -2222,11 +2246,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
22222246 }
22232247 }
22242248
2225- if (!pblocktree->WriteBlockFileInfo (nLastBlockFile, vinfoBlockFile[nFile]))
2226- return state.Abort (" Failed to write file info" );
2227- if (fUpdatedLast )
2228- pblocktree->WriteLastBlockFile (nLastBlockFile);
2229-
2249+ setDirtyFileInfo.insert (nFile);
22302250 return true ;
22312251}
22322252
@@ -2239,9 +2259,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
22392259 unsigned int nNewSize;
22402260 pos.nPos = vinfoBlockFile[nFile].nUndoSize ;
22412261 nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
2242- if (!pblocktree->WriteBlockFileInfo (nLastBlockFile, vinfoBlockFile[nLastBlockFile])) {
2243- return state.Abort (" Failed to write block info" );
2244- }
2262+ setDirtyFileInfo.insert (nFile);
22452263
22462264 unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
22472265 unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
@@ -2462,6 +2480,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
24622480 if ((!CheckBlock (block, state)) || !ContextualCheckBlock (block, state, pindex->pprev )) {
24632481 if (state.IsInvalid () && !state.CorruptionPossible ()) {
24642482 pindex->nStatus |= BLOCK_FAILED_VALID;
2483+ setDirtyBlockIndex.insert (pindex);
24652484 }
24662485 return false ;
24672486 }
@@ -3070,7 +3089,7 @@ bool InitBlockIndex() {
30703089 if (!ActivateBestChain (state, &block))
30713090 return error (" LoadBlockIndex() : genesis block cannot be activated" );
30723091 // Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data
3073- return WriteChainState (state, true );
3092+ return FlushStateToDisk (state, false , true );
30743093 } catch (std::runtime_error &e) {
30753094 return error (" LoadBlockIndex() : failed to initialize block database: %s" , e.what ());
30763095 }
@@ -4641,11 +4660,6 @@ bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock)
46414660 hasher << *this ;
46424661 fileout << hasher.GetHash ();
46434662
4644- // Flush stdio buffers and commit to disk before returning
4645- fflush (fileout.Get ());
4646- if (!IsInitialBlockDownload ())
4647- FileCommit (fileout.Get ());
4648-
46494663 return true ;
46504664}
46514665
0 commit comments