@@ -130,6 +130,12 @@ namespace {
130
130
131
131
// Number of preferrable block download peers.
132
132
int nPreferredDownload = 0 ;
133
+
134
+ // Dirty block index entries.
135
+ set<CBlockIndex*> setDirtyBlockIndex;
136
+
137
+ // Dirty block file entries.
138
+ set<int > setDirtyFileInfo;
133
139
} // anon namespace
134
140
135
141
// ////////////////////////////////////////////////////////////////////////////
@@ -1137,11 +1143,6 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos)
1137
1143
pos.nPos = (unsigned int )fileOutPos;
1138
1144
fileout << block;
1139
1145
1140
- // Flush stdio buffers and commit to disk before returning
1141
- fflush (fileout.Get ());
1142
- if (!IsInitialBlockDownload ())
1143
- FileCommit (fileout.Get ());
1144
-
1145
1146
return true ;
1146
1147
}
1147
1148
@@ -1335,7 +1336,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
1335
1336
}
1336
1337
if (!state.CorruptionPossible ()) {
1337
1338
pindex->nStatus |= BLOCK_FAILED_VALID;
1338
- pblocktree-> WriteBlockIndex ( CDiskBlockIndex ( pindex) );
1339
+ setDirtyBlockIndex. insert ( pindex);
1339
1340
setBlockIndexCandidates.erase (pindex);
1340
1341
InvalidChainFound (pindex);
1341
1342
}
@@ -1732,10 +1733,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
1732
1733
}
1733
1734
1734
1735
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);
1739
1737
}
1740
1738
1741
1739
if (fTxIndex )
@@ -1759,26 +1757,68 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
1759
1757
return true ;
1760
1758
}
1761
1759
1762
- // Update the on-disk chain state.
1763
- bool static WriteChainState (CValidationState &state, bool forceWrite=false ) {
1760
+ enum FlushStateMode {
1761
+ FLUSH_STATE_IF_NEEDED,
1762
+ FLUSH_STATE_PERIODIC,
1763
+ FLUSH_STATE_ALWAYS
1764
+ };
1765
+
1766
+ /* *
1767
+ * Update the on-disk chain state.
1768
+ * The caches and indexes are flushed if either they're too large, forceWrite is set, or
1769
+ * fast is not set and it's been a while since the last write.
1770
+ */
1771
+ bool static FlushStateToDisk (CValidationState &state, FlushStateMode mode) {
1772
+ LOCK (cs_main);
1764
1773
static int64_t nLastWrite = 0 ;
1765
- if (forceWrite || pcoinsTip->GetCacheSize () > nCoinCacheSize || (!IsInitialBlockDownload () && GetTimeMicros () > nLastWrite + 600 *1000000 )) {
1774
+ if ((mode == FLUSH_STATE_ALWAYS) ||
1775
+ ((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->GetCacheSize () > nCoinCacheSize) ||
1776
+ (mode == FLUSH_STATE_PERIODIC && GetTimeMicros () > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000 )) {
1766
1777
// Typical CCoins structures on disk are around 100 bytes in size.
1767
1778
// Pushing a new one to the database can cause it to be written
1768
1779
// twice (once in the log, and once in the tables). This is already
1769
1780
// an overestimation, as most will delete an existing entry or
1770
1781
// overwrite one. Still, use a conservative safety factor of 2.
1771
1782
if (!CheckDiskSpace (100 * 2 * 2 * pcoinsTip->GetCacheSize ()))
1772
1783
return state.Error (" out of disk space" );
1784
+ // First make sure all block and undo data is flushed to disk.
1773
1785
FlushBlockFile ();
1786
+ // Then update all block file information (which may refer to block and undo files).
1787
+ bool fileschanged = false ;
1788
+ for (set<int >::iterator it = setDirtyFileInfo.begin (); it != setDirtyFileInfo.end (); ) {
1789
+ if (!pblocktree->WriteBlockFileInfo (*it, vinfoBlockFile[*it])) {
1790
+ return state.Abort (" Failed to write to block index" );
1791
+ }
1792
+ fileschanged = true ;
1793
+ setDirtyFileInfo.erase (it++);
1794
+ }
1795
+ if (fileschanged && !pblocktree->WriteLastBlockFile (nLastBlockFile)) {
1796
+ return state.Abort (" Failed to write to block index" );
1797
+ }
1798
+ for (set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin (); it != setDirtyBlockIndex.end (); ) {
1799
+ if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (*it))) {
1800
+ return state.Abort (" Failed to write to block index" );
1801
+ }
1802
+ setDirtyBlockIndex.erase (it++);
1803
+ }
1774
1804
pblocktree->Sync ();
1805
+ // Finally flush the chainstate (which may refer to block index entries).
1775
1806
if (!pcoinsTip->Flush ())
1776
1807
return state.Abort (" Failed to write to coin database" );
1808
+ // Update best block in wallet (so we can detect restored wallets).
1809
+ if (mode != FLUSH_STATE_IF_NEEDED) {
1810
+ g_signals.SetBestChain (chainActive.GetLocator ());
1811
+ }
1777
1812
nLastWrite = GetTimeMicros ();
1778
1813
}
1779
1814
return true ;
1780
1815
}
1781
1816
1817
+ void FlushStateToDisk () {
1818
+ CValidationState state;
1819
+ FlushStateToDisk (state, FLUSH_STATE_ALWAYS);
1820
+ }
1821
+
1782
1822
// Update chainActive and related internal data structures.
1783
1823
void static UpdateTip (CBlockIndex *pindexNew) {
1784
1824
chainActive.SetTip (pindexNew);
@@ -1837,7 +1877,7 @@ bool static DisconnectTip(CValidationState &state) {
1837
1877
}
1838
1878
LogPrint (" bench" , " - Disconnect block: %.2fms\n " , (GetTimeMicros () - nStart) * 0.001 );
1839
1879
// Write the chain state to disk, if necessary.
1840
- if (!WriteChainState (state))
1880
+ if (!FlushStateToDisk (state, FLUSH_STATE_IF_NEEDED ))
1841
1881
return false ;
1842
1882
// Resurrect mempool transactions from the disconnected block.
1843
1883
BOOST_FOREACH (const CTransaction &tx, block.vtx ) {
@@ -1900,7 +1940,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
1900
1940
int64_t nTime4 = GetTimeMicros (); nTimeFlush += nTime4 - nTime3;
1901
1941
LogPrint (" bench" , " - Flush: %.2fms [%.2fs]\n " , (nTime4 - nTime3) * 0.001 , nTimeFlush * 0.000001 );
1902
1942
// Write the chain state to disk, if necessary.
1903
- if (!WriteChainState (state))
1943
+ if (!FlushStateToDisk (state, FLUSH_STATE_IF_NEEDED ))
1904
1944
return false ;
1905
1945
int64_t nTime5 = GetTimeMicros (); nTimeChainState += nTime5 - nTime4;
1906
1946
LogPrint (" bench" , " - Writing chainstate: %.2fms [%.2fs]\n " , (nTime5 - nTime4) * 0.001 , nTimeChainState * 0.000001 );
@@ -1919,10 +1959,6 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
1919
1959
BOOST_FOREACH (const CTransaction &tx, pblock->vtx ) {
1920
1960
SyncWithWallets (tx, pblock);
1921
1961
}
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 ());
1926
1962
1927
1963
int64_t nTime6 = GetTimeMicros (); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
1928
1964
LogPrint (" bench" , " - Connect postprocess: %.2fms [%.2fs]\n " , (nTime6 - nTime5) * 0.001 , nTimePostConnect * 0.000001 );
@@ -2043,9 +2079,6 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo
2043
2079
else
2044
2080
CheckForkWarningConditions ();
2045
2081
2046
- if (!pblocktree->Flush ())
2047
- return state.Abort (" Failed to sync block index" );
2048
-
2049
2082
return true ;
2050
2083
}
2051
2084
@@ -2086,11 +2119,16 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
2086
2119
if (chainActive.Height () > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
2087
2120
pnode->PushInventory (CInv (MSG_BLOCK, hashNewTip));
2088
2121
}
2089
-
2122
+ // Notify external listeners about the new tip.
2090
2123
uiInterface.NotifyBlockTip (hashNewTip);
2091
2124
}
2092
2125
} while (pindexMostWork != chainActive.Tip ());
2093
2126
2127
+ // Write changes periodically to disk, after relay.
2128
+ if (!FlushStateToDisk (state, FLUSH_STATE_PERIODIC)) {
2129
+ return false ;
2130
+ }
2131
+
2094
2132
return true ;
2095
2133
}
2096
2134
@@ -2123,8 +2161,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
2123
2161
if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork )
2124
2162
pindexBestHeader = pindexNew;
2125
2163
2126
- // Ok if it fails, we'll download the header again next time.
2127
- pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew));
2164
+ setDirtyBlockIndex.insert (pindexNew);
2128
2165
2129
2166
return pindexNew;
2130
2167
}
@@ -2143,6 +2180,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
2143
2180
LOCK (cs_nBlockSequenceId);
2144
2181
pindexNew->nSequenceId = nBlockSequenceId++;
2145
2182
}
2183
+ setDirtyBlockIndex.insert (pindexNew);
2146
2184
2147
2185
if (pindexNew->pprev == NULL || pindexNew->pprev ->nChainTx ) {
2148
2186
// If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS.
@@ -2162,24 +2200,18 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
2162
2200
range.first ++;
2163
2201
mapBlocksUnlinked.erase (it);
2164
2202
}
2165
- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindex)))
2166
- return state.Abort (" Failed to write block index" );
2167
2203
}
2168
2204
} else {
2169
2205
if (pindexNew->pprev && pindexNew->pprev ->IsValid (BLOCK_VALID_TREE)) {
2170
2206
mapBlocksUnlinked.insert (std::make_pair (pindexNew->pprev , pindexNew));
2171
2207
}
2172
- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew)))
2173
- return state.Abort (" Failed to write block index" );
2174
2208
}
2175
2209
2176
2210
return true ;
2177
2211
}
2178
2212
2179
2213
bool FindBlockPos (CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false )
2180
2214
{
2181
- bool fUpdatedLast = false ;
2182
-
2183
2215
LOCK (cs_LastBlockFile);
2184
2216
2185
2217
unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
@@ -2195,7 +2227,6 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
2195
2227
if (vinfoBlockFile.size () <= nFile) {
2196
2228
vinfoBlockFile.resize (nFile + 1 );
2197
2229
}
2198
- fUpdatedLast = true ;
2199
2230
}
2200
2231
pos.nFile = nFile;
2201
2232
pos.nPos = vinfoBlockFile[nFile].nSize ;
@@ -2222,11 +2253,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
2222
2253
}
2223
2254
}
2224
2255
2225
- if (!pblocktree->WriteBlockFileInfo (nLastBlockFile, vinfoBlockFile[nFile]))
2226
- return state.Abort (" Failed to write file info" );
2227
- if (fUpdatedLast )
2228
- pblocktree->WriteLastBlockFile (nLastBlockFile);
2229
-
2256
+ setDirtyFileInfo.insert (nFile);
2230
2257
return true ;
2231
2258
}
2232
2259
@@ -2239,9 +2266,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
2239
2266
unsigned int nNewSize;
2240
2267
pos.nPos = vinfoBlockFile[nFile].nUndoSize ;
2241
2268
nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize;
2242
- if (!pblocktree->WriteBlockFileInfo (nLastBlockFile, vinfoBlockFile[nLastBlockFile])) {
2243
- return state.Abort (" Failed to write block info" );
2244
- }
2269
+ setDirtyFileInfo.insert (nFile);
2245
2270
2246
2271
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
2247
2272
unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
@@ -2462,6 +2487,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
2462
2487
if ((!CheckBlock (block, state)) || !ContextualCheckBlock (block, state, pindex->pprev )) {
2463
2488
if (state.IsInvalid () && !state.CorruptionPossible ()) {
2464
2489
pindex->nStatus |= BLOCK_FAILED_VALID;
2490
+ setDirtyBlockIndex.insert (pindex);
2465
2491
}
2466
2492
return false ;
2467
2493
}
@@ -3070,7 +3096,7 @@ bool InitBlockIndex() {
3070
3096
if (!ActivateBestChain (state, &block))
3071
3097
return error (" LoadBlockIndex() : genesis block cannot be activated" );
3072
3098
// Force a chainstate write so that when we VerifyDB in a moment, it doesnt check stale data
3073
- return WriteChainState (state, true );
3099
+ return FlushStateToDisk (state, FLUSH_STATE_ALWAYS );
3074
3100
} catch (std::runtime_error &e) {
3075
3101
return error (" LoadBlockIndex() : failed to initialize block database: %s" , e.what ());
3076
3102
}
@@ -4641,11 +4667,6 @@ bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock)
4641
4667
hasher << *this ;
4642
4668
fileout << hasher.GetHash ();
4643
4669
4644
- // Flush stdio buffers and commit to disk before returning
4645
- fflush (fileout.Get ());
4646
- if (!IsInitialBlockDownload ())
4647
- FileCommit (fileout.Get ());
4648
-
4649
4670
return true ;
4650
4671
}
4651
4672
0 commit comments