Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 2,218 Lines • ▼ Show 20 Lines | |||||
* The caches and indexes are flushed depending on the mode we're called with if | * The caches and indexes are flushed depending on the mode we're called with if | ||||
* they're too large, if it's been a while since the last write, or always and | * they're too large, if it's been a while since the last write, or always and | ||||
* in all cases if we're in prune mode and are deleting files. | * in all cases if we're in prune mode and are deleting files. | ||||
*/ | */ | ||||
static bool FlushStateToDisk(const CChainParams &chainparams, | static bool FlushStateToDisk(const CChainParams &chainparams, | ||||
CValidationState &state, FlushStateMode mode, | CValidationState &state, FlushStateMode mode, | ||||
int nManualPruneHeight) { | int nManualPruneHeight) { | ||||
int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); | int64_t nMempoolUsage = mempool.DynamicMemoryUsage(); | ||||
LOCK2(cs_main, cs_LastBlockFile); | LOCK(cs_main); | ||||
static int64_t nLastWrite = 0; | static int64_t nLastWrite = 0; | ||||
static int64_t nLastFlush = 0; | static int64_t nLastFlush = 0; | ||||
static int64_t nLastSetChain = 0; | static int64_t nLastSetChain = 0; | ||||
std::set<int> setFilesToPrune; | std::set<int> setFilesToPrune; | ||||
bool fFlushForPrune = false; | bool fFlushForPrune = false; | ||||
bool fDoFullFlush = false; | |||||
int64_t nNow = 0; | |||||
try { | try { | ||||
{ | |||||
LOCK(cs_LastBlockFile); | |||||
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && | if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && | ||||
!fReindex) { | !fReindex) { | ||||
if (nManualPruneHeight > 0) { | if (nManualPruneHeight > 0) { | ||||
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); | FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); | ||||
} else { | } else { | ||||
FindFilesToPrune(setFilesToPrune, | FindFilesToPrune(setFilesToPrune, | ||||
chainparams.PruneAfterHeight()); | chainparams.PruneAfterHeight()); | ||||
fCheckForPruning = false; | fCheckForPruning = false; | ||||
} | } | ||||
if (!setFilesToPrune.empty()) { | if (!setFilesToPrune.empty()) { | ||||
fFlushForPrune = true; | fFlushForPrune = true; | ||||
if (!fHavePruned) { | if (!fHavePruned) { | ||||
pblocktree->WriteFlag("prunedblockfiles", true); | pblocktree->WriteFlag("prunedblockfiles", true); | ||||
fHavePruned = true; | fHavePruned = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
int64_t nNow = GetTimeMicros(); | nNow = GetTimeMicros(); | ||||
// Avoid writing/flushing immediately after startup. | // Avoid writing/flushing immediately after startup. | ||||
if (nLastWrite == 0) { | if (nLastWrite == 0) { | ||||
nLastWrite = nNow; | nLastWrite = nNow; | ||||
} | } | ||||
if (nLastFlush == 0) { | if (nLastFlush == 0) { | ||||
nLastFlush = nNow; | nLastFlush = nNow; | ||||
} | } | ||||
if (nLastSetChain == 0) { | if (nLastSetChain == 0) { | ||||
nLastSetChain = nNow; | nLastSetChain = nNow; | ||||
} | } | ||||
int64_t nMempoolSizeMax = | int64_t nMempoolSizeMax = | ||||
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; | gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; | ||||
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); | int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); | ||||
int64_t nTotalSpace = | int64_t nTotalSpace = | ||||
nCoinCacheUsage + | nCoinCacheUsage + | ||||
std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); | std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); | ||||
// The cache is large and we're within 10% and 10 MiB of the limit, but | // The cache is large and we're within 10% and 10 MiB of the limit, | ||||
// we have time now (not in the middle of a block processing). | // but we have time now (not in the middle of a block processing). | ||||
bool fCacheLarge = | bool fCacheLarge = | ||||
mode == FLUSH_STATE_PERIODIC && | mode == FLUSH_STATE_PERIODIC && | ||||
cacheSize > | cacheSize > std::max((9 * nTotalSpace) / 10, | ||||
std::max((9 * nTotalSpace) / 10, | nTotalSpace - | ||||
nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); | MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); | ||||
// The cache is over the limit, we have to write now. | // The cache is over the limit, we have to write now. | ||||
bool fCacheCritical = | bool fCacheCritical = | ||||
mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; | mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; | ||||
// It's been a while since we wrote the block index to disk. Do this | // It's been a while since we wrote the block index to disk. Do this | ||||
// frequently, so we don't need to redownload after a crash. | // frequently, so we don't need to redownload after a crash. | ||||
bool fPeriodicWrite = | bool fPeriodicWrite = | ||||
mode == FLUSH_STATE_PERIODIC && | mode == FLUSH_STATE_PERIODIC && | ||||
nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; | nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; | ||||
// It's been very long since we flushed the cache. Do this infrequently, | // It's been very long since we flushed the cache. Do this | ||||
// to optimize cache usage. | // infrequently, to optimize cache usage. | ||||
bool fPeriodicFlush = | bool fPeriodicFlush = | ||||
mode == FLUSH_STATE_PERIODIC && | mode == FLUSH_STATE_PERIODIC && | ||||
nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; | nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; | ||||
// Combine all conditions that result in a full cache flush. | // Combine all conditions that result in a full cache flush. | ||||
bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || | fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || | ||||
fCacheCritical || fPeriodicFlush || fFlushForPrune; | fCacheCritical || fPeriodicFlush || fFlushForPrune; | ||||
// Write blocks and block index to disk. | // Write blocks and block index to disk. | ||||
if (fDoFullFlush || fPeriodicWrite) { | if (fDoFullFlush || fPeriodicWrite) { | ||||
// Depend on nMinDiskSpace to ensure we can write block index | // Depend on nMinDiskSpace to ensure we can write block index | ||||
if (!CheckDiskSpace(0)) return state.Error("out of disk space"); | if (!CheckDiskSpace(0)) { | ||||
return state.Error("out of disk space"); | |||||
} | |||||
// First make sure all block and undo data is flushed to disk. | // First make sure all block and undo data is flushed to disk. | ||||
FlushBlockFile(); | FlushBlockFile(); | ||||
// Then update all block file information (which may refer to block | // Then update all block file information (which may refer to | ||||
// and undo files). | // block and undo files). | ||||
{ | { | ||||
std::vector<std::pair<int, const CBlockFileInfo *>> vFiles; | std::vector<std::pair<int, const CBlockFileInfo *>> vFiles; | ||||
vFiles.reserve(setDirtyFileInfo.size()); | vFiles.reserve(setDirtyFileInfo.size()); | ||||
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); | for (std::set<int>::iterator it = setDirtyFileInfo.begin(); | ||||
it != setDirtyFileInfo.end();) { | it != setDirtyFileInfo.end();) { | ||||
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); | vFiles.push_back( | ||||
std::make_pair(*it, &vinfoBlockFile[*it])); | |||||
setDirtyFileInfo.erase(it++); | setDirtyFileInfo.erase(it++); | ||||
} | } | ||||
std::vector<const CBlockIndex *> vBlocks; | std::vector<const CBlockIndex *> vBlocks; | ||||
vBlocks.reserve(setDirtyBlockIndex.size()); | vBlocks.reserve(setDirtyBlockIndex.size()); | ||||
for (std::set<CBlockIndex *>::iterator it = | for (std::set<CBlockIndex *>::iterator it = | ||||
setDirtyBlockIndex.begin(); | setDirtyBlockIndex.begin(); | ||||
it != setDirtyBlockIndex.end();) { | it != setDirtyBlockIndex.end();) { | ||||
vBlocks.push_back(*it); | vBlocks.push_back(*it); | ||||
setDirtyBlockIndex.erase(it++); | setDirtyBlockIndex.erase(it++); | ||||
} | } | ||||
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, | if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, | ||||
vBlocks)) { | vBlocks)) { | ||||
return AbortNode(state, | return AbortNode( | ||||
"Failed to write to block index database"); | state, "Failed to write to block index database"); | ||||
} | } | ||||
} | } | ||||
// Finally remove any pruned files | // Finally remove any pruned files | ||||
if (fFlushForPrune) UnlinkPrunedFiles(setFilesToPrune); | if (fFlushForPrune) UnlinkPrunedFiles(setFilesToPrune); | ||||
nLastWrite = nNow; | nLastWrite = nNow; | ||||
} | } | ||||
// Flush best chain related state. This can only be done if the blocks / | // Flush best chain related state. This can only be done if the | ||||
// block index write was also done. | // blocks / block index write was also done. | ||||
if (fDoFullFlush) { | if (fDoFullFlush) { | ||||
// Typical Coin structures on disk are around 48 bytes in size. | // Typical Coin structures on disk are around 48 bytes in size. | ||||
// Pushing a new one to the database can cause it to be written | // Pushing a new one to the database can cause it to be written | ||||
// twice (once in the log, and once in the tables). This is already | // twice (once in the log, and once in the tables). This is | ||||
// an overestimation, as most will delete an existing entry or | // already an overestimation, as most will delete an existing | ||||
// overwrite one. Still, use a conservative safety factor of 2. | // entry or overwrite one. Still, use a conservative safety | ||||
// factor of 2. | |||||
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) { | if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) { | ||||
return state.Error("out of disk space"); | return state.Error("out of disk space"); | ||||
} | } | ||||
// Flush the chainstate (which may refer to block index entries). | // Flush the chainstate (which may refer to block index | ||||
// entries). | |||||
if (!pcoinsTip->Flush()) { | if (!pcoinsTip->Flush()) { | ||||
return AbortNode(state, "Failed to write to coin database"); | return AbortNode(state, "Failed to write to coin database"); | ||||
} | } | ||||
nLastFlush = nNow; | nLastFlush = nNow; | ||||
} | } | ||||
} | |||||
if (fDoFullFlush || | if (fDoFullFlush || | ||||
((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && | ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && | ||||
nNow > | nNow > | ||||
nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { | nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { | ||||
// Update best block in wallet (so we can detect restored wallets). | // Update best block in wallet (so we can detect restored wallets). | ||||
GetMainSignals().SetBestChain(chainActive.GetLocator()); | GetMainSignals().SetBestChain(chainActive.GetLocator()); | ||||
nLastSetChain = nNow; | nLastSetChain = nNow; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,962 Lines • Show Last 20 Lines |