@@ -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,61 @@ 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
+ /* *
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);
1764
1767
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 )) {
1766
1770
// Typical CCoins structures on disk are around 100 bytes in size.
1767
1771
// Pushing a new one to the database can cause it to be written
1768
1772
// twice (once in the log, and once in the tables). This is already
1769
1773
// an overestimation, as most will delete an existing entry or
1770
1774
// overwrite one. Still, use a conservative safety factor of 2.
1771
1775
if (!CheckDiskSpace (100 * 2 * 2 * pcoinsTip->GetCacheSize ()))
1772
1776
return state.Error (" out of disk space" );
1777
+ // First make sure all block and undo data is flushed to disk.
1773
1778
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
+ }
1774
1797
pblocktree->Sync ();
1798
+ // Finally flush the chainstate (which may refer to block index entries).
1775
1799
if (!pcoinsTip->Flush ())
1776
1800
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
+ }
1777
1805
nLastWrite = GetTimeMicros ();
1778
1806
}
1779
1807
return true ;
1780
1808
}
1781
1809
1810
+ void FlushStateToDisk () {
1811
+ CValidationState state;
1812
+ FlushStateToDisk (state, false , true );
1813
+ }
1814
+
1782
1815
// Update chainActive and related internal data structures.
1783
1816
void static UpdateTip (CBlockIndex *pindexNew) {
1784
1817
chainActive.SetTip (pindexNew);
@@ -1837,7 +1870,7 @@ bool static DisconnectTip(CValidationState &state) {
1837
1870
}
1838
1871
LogPrint (" bench" , " - Disconnect block: %.2fms\n " , (GetTimeMicros () - nStart) * 0.001 );
1839
1872
// Write the chain state to disk, if necessary.
1840
- if (!WriteChainState (state))
1873
+ if (!FlushStateToDisk (state, true ))
1841
1874
return false ;
1842
1875
// Resurrect mempool transactions from the disconnected block.
1843
1876
BOOST_FOREACH (const CTransaction &tx, block.vtx ) {
@@ -1900,7 +1933,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
1900
1933
int64_t nTime4 = GetTimeMicros (); nTimeFlush += nTime4 - nTime3;
1901
1934
LogPrint (" bench" , " - Flush: %.2fms [%.2fs]\n " , (nTime4 - nTime3) * 0.001 , nTimeFlush * 0.000001 );
1902
1935
// Write the chain state to disk, if necessary.
1903
- if (!WriteChainState (state))
1936
+ if (!FlushStateToDisk (state, true ))
1904
1937
return false ;
1905
1938
int64_t nTime5 = GetTimeMicros (); nTimeChainState += nTime5 - nTime4;
1906
1939
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 *
1919
1952
BOOST_FOREACH (const CTransaction &tx, pblock->vtx ) {
1920
1953
SyncWithWallets (tx, pblock);
1921
1954
}
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
1955
1927
1956
int64_t nTime6 = GetTimeMicros (); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
1928
1957
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
2043
2072
else
2044
2073
CheckForkWarningConditions ();
2045
2074
2046
- if (!pblocktree->Flush ())
2047
- return state.Abort (" Failed to sync block index" );
2048
-
2049
2075
return true ;
2050
2076
}
2051
2077
@@ -2086,11 +2112,16 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
2086
2112
if (chainActive.Height () > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
2087
2113
pnode->PushInventory (CInv (MSG_BLOCK, hashNewTip));
2088
2114
}
2089
-
2115
+ // Notify external listeners about the new tip.
2090
2116
uiInterface.NotifyBlockTip (hashNewTip);
2091
2117
}
2092
2118
} while (pindexMostWork != chainActive.Tip ());
2093
2119
2120
+ // Write changes periodically to disk, after relay.
2121
+ if (!FlushStateToDisk (state)) {
2122
+ return false ;
2123
+ }
2124
+
2094
2125
return true ;
2095
2126
}
2096
2127
@@ -2123,8 +2154,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block)
2123
2154
if (pindexBestHeader == NULL || pindexBestHeader->nChainWork < pindexNew->nChainWork )
2124
2155
pindexBestHeader = pindexNew;
2125
2156
2126
- // Ok if it fails, we'll download the header again next time.
2127
- pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew));
2157
+ setDirtyBlockIndex.insert (pindexNew);
2128
2158
2129
2159
return pindexNew;
2130
2160
}
@@ -2143,6 +2173,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
2143
2173
LOCK (cs_nBlockSequenceId);
2144
2174
pindexNew->nSequenceId = nBlockSequenceId++;
2145
2175
}
2176
+ setDirtyBlockIndex.insert (pindexNew);
2146
2177
2147
2178
if (pindexNew->pprev == NULL || pindexNew->pprev ->nChainTx ) {
2148
2179
// 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
2162
2193
range.first ++;
2163
2194
mapBlocksUnlinked.erase (it);
2164
2195
}
2165
- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindex)))
2166
- return state.Abort (" Failed to write block index" );
2167
2196
}
2168
2197
} else {
2169
2198
if (pindexNew->pprev && pindexNew->pprev ->IsValid (BLOCK_VALID_TREE)) {
2170
2199
mapBlocksUnlinked.insert (std::make_pair (pindexNew->pprev , pindexNew));
2171
2200
}
2172
- if (!pblocktree->WriteBlockIndex (CDiskBlockIndex (pindexNew)))
2173
- return state.Abort (" Failed to write block index" );
2174
2201
}
2175
2202
2176
2203
return true ;
2177
2204
}
2178
2205
2179
2206
bool FindBlockPos (CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false )
2180
2207
{
2181
- bool fUpdatedLast = false ;
2182
-
2183
2208
LOCK (cs_LastBlockFile);
2184
2209
2185
2210
unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile;
@@ -2195,7 +2220,6 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
2195
2220
if (vinfoBlockFile.size () <= nFile) {
2196
2221
vinfoBlockFile.resize (nFile + 1 );
2197
2222
}
2198
- fUpdatedLast = true ;
2199
2223
}
2200
2224
pos.nFile = nFile;
2201
2225
pos.nPos = vinfoBlockFile[nFile].nSize ;
@@ -2222,11 +2246,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd
2222
2246
}
2223
2247
}
2224
2248
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);
2230
2250
return true ;
2231
2251
}
2232
2252
@@ -2239,9 +2259,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
2239
2259
unsigned int nNewSize;
2240
2260
pos.nPos = vinfoBlockFile[nFile].nUndoSize ;
2241
2261
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);
2245
2263
2246
2264
unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
2247
2265
unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1 ) / UNDOFILE_CHUNK_SIZE;
@@ -2462,6 +2480,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
2462
2480
if ((!CheckBlock (block, state)) || !ContextualCheckBlock (block, state, pindex->pprev )) {
2463
2481
if (state.IsInvalid () && !state.CorruptionPossible ()) {
2464
2482
pindex->nStatus |= BLOCK_FAILED_VALID;
2483
+ setDirtyBlockIndex.insert (pindex);
2465
2484
}
2466
2485
return false ;
2467
2486
}
@@ -3070,7 +3089,7 @@ bool InitBlockIndex() {
3070
3089
if (!ActivateBestChain (state, &block))
3071
3090
return error (" LoadBlockIndex() : genesis block cannot be activated" );
3072
3091
// 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 );
3074
3093
} catch (std::runtime_error &e) {
3075
3094
return error (" LoadBlockIndex() : failed to initialize block database: %s" , e.what ());
3076
3095
}
@@ -4641,11 +4660,6 @@ bool CBlockUndo::WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock)
4641
4660
hasher << *this ;
4642
4661
fileout << hasher.GetHash ();
4643
4662
4644
- // Flush stdio buffers and commit to disk before returning
4645
- fflush (fileout.Get ());
4646
- if (!IsInitialBlockDownload ())
4647
- FileCommit (fileout.Get ());
4648
-
4649
4663
return true ;
4650
4664
}
4651
4665
0 commit comments