|
|
@ -1863,95 +1863,100 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) { |
|
|
|
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, 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 { |
|
|
|
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { |
|
|
|
{ |
|
|
|
if (nManualPruneHeight > 0) { |
|
|
|
LOCK(cs_LastBlockFile); |
|
|
|
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); |
|
|
|
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) { |
|
|
|
} else { |
|
|
|
if (nManualPruneHeight > 0) { |
|
|
|
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); |
|
|
|
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight); |
|
|
|
fCheckForPruning = false; |
|
|
|
} else { |
|
|
|
} |
|
|
|
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight()); |
|
|
|
if (!setFilesToPrune.empty()) { |
|
|
|
fCheckForPruning = false; |
|
|
|
fFlushForPrune = true; |
|
|
|
|
|
|
|
if (!fHavePruned) { |
|
|
|
|
|
|
|
pblocktree->WriteFlag("prunedblockfiles", true); |
|
|
|
|
|
|
|
fHavePruned = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int64_t nNow = GetTimeMicros(); |
|
|
|
|
|
|
|
// Avoid writing/flushing immediately after startup.
|
|
|
|
|
|
|
|
if (nLastWrite == 0) { |
|
|
|
|
|
|
|
nLastWrite = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (nLastFlush == 0) { |
|
|
|
|
|
|
|
nLastFlush = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (nLastSetChain == 0) { |
|
|
|
|
|
|
|
nLastSetChain = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; |
|
|
|
|
|
|
|
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); |
|
|
|
|
|
|
|
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); |
|
|
|
|
|
|
|
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
|
|
|
|
|
|
|
|
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); |
|
|
|
|
|
|
|
// The cache is over the limit, we have to write now.
|
|
|
|
|
|
|
|
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; |
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; |
|
|
|
|
|
|
|
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
|
|
|
|
|
|
|
|
bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; |
|
|
|
|
|
|
|
// Combine all conditions that result in a full cache flush.
|
|
|
|
|
|
|
|
bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; |
|
|
|
|
|
|
|
// Write blocks and block index to disk.
|
|
|
|
|
|
|
|
if (fDoFullFlush || fPeriodicWrite) { |
|
|
|
|
|
|
|
// Depend on nMinDiskSpace to ensure we can write block index
|
|
|
|
|
|
|
|
if (!CheckDiskSpace(0)) |
|
|
|
|
|
|
|
return state.Error("out of disk space"); |
|
|
|
|
|
|
|
// First make sure all block and undo data is flushed to disk.
|
|
|
|
|
|
|
|
FlushBlockFile(); |
|
|
|
|
|
|
|
// Then update all block file information (which may refer to block and undo files).
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles; |
|
|
|
|
|
|
|
vFiles.reserve(setDirtyFileInfo.size()); |
|
|
|
|
|
|
|
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { |
|
|
|
|
|
|
|
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); |
|
|
|
|
|
|
|
setDirtyFileInfo.erase(it++); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
std::vector<const CBlockIndex*> vBlocks; |
|
|
|
if (!setFilesToPrune.empty()) { |
|
|
|
vBlocks.reserve(setDirtyBlockIndex.size()); |
|
|
|
fFlushForPrune = true; |
|
|
|
for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { |
|
|
|
if (!fHavePruned) { |
|
|
|
vBlocks.push_back(*it); |
|
|
|
pblocktree->WriteFlag("prunedblockfiles", true); |
|
|
|
setDirtyBlockIndex.erase(it++); |
|
|
|
fHavePruned = true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { |
|
|
|
} |
|
|
|
return AbortNode(state, "Failed to write to block index database"); |
|
|
|
nNow = GetTimeMicros(); |
|
|
|
|
|
|
|
// Avoid writing/flushing immediately after startup.
|
|
|
|
|
|
|
|
if (nLastWrite == 0) { |
|
|
|
|
|
|
|
nLastWrite = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (nLastFlush == 0) { |
|
|
|
|
|
|
|
nLastFlush = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (nLastSetChain == 0) { |
|
|
|
|
|
|
|
nLastSetChain = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; |
|
|
|
|
|
|
|
int64_t cacheSize = pcoinsTip->DynamicMemoryUsage(); |
|
|
|
|
|
|
|
int64_t nTotalSpace = nCoinCacheUsage + std::max<int64_t>(nMempoolSizeMax - nMempoolUsage, 0); |
|
|
|
|
|
|
|
// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).
|
|
|
|
|
|
|
|
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize > std::max((9 * nTotalSpace) / 10, nTotalSpace - MAX_BLOCK_COINSDB_USAGE * 1024 * 1024); |
|
|
|
|
|
|
|
// The cache is over the limit, we have to write now.
|
|
|
|
|
|
|
|
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nTotalSpace; |
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000; |
|
|
|
|
|
|
|
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
|
|
|
|
|
|
|
|
bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000; |
|
|
|
|
|
|
|
// Combine all conditions that result in a full cache flush.
|
|
|
|
|
|
|
|
fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune; |
|
|
|
|
|
|
|
// Write blocks and block index to disk.
|
|
|
|
|
|
|
|
if (fDoFullFlush || fPeriodicWrite) { |
|
|
|
|
|
|
|
// Depend on nMinDiskSpace to ensure we can write block index
|
|
|
|
|
|
|
|
if (!CheckDiskSpace(0)) |
|
|
|
|
|
|
|
return state.Error("out of disk space"); |
|
|
|
|
|
|
|
// First make sure all block and undo data is flushed to disk.
|
|
|
|
|
|
|
|
FlushBlockFile(); |
|
|
|
|
|
|
|
// Then update all block file information (which may refer to block and undo files).
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles; |
|
|
|
|
|
|
|
vFiles.reserve(setDirtyFileInfo.size()); |
|
|
|
|
|
|
|
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { |
|
|
|
|
|
|
|
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it])); |
|
|
|
|
|
|
|
setDirtyFileInfo.erase(it++); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::vector<const CBlockIndex*> vBlocks; |
|
|
|
|
|
|
|
vBlocks.reserve(setDirtyBlockIndex.size()); |
|
|
|
|
|
|
|
for (std::set<CBlockIndex*>::iterator it = setDirtyBlockIndex.begin(); it != setDirtyBlockIndex.end(); ) { |
|
|
|
|
|
|
|
vBlocks.push_back(*it); |
|
|
|
|
|
|
|
setDirtyBlockIndex.erase(it++); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks)) { |
|
|
|
|
|
|
|
return AbortNode(state, "Failed to write to block index database"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Finally remove any pruned files
|
|
|
|
|
|
|
|
if (fFlushForPrune) |
|
|
|
|
|
|
|
UnlinkPrunedFiles(setFilesToPrune); |
|
|
|
|
|
|
|
nLastWrite = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
|
|
|
|
|
|
|
|
if (fDoFullFlush) { |
|
|
|
|
|
|
|
// Typical Coin structures on disk are around 48 bytes in size.
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// an overestimation, as most will delete an existing entry or
|
|
|
|
|
|
|
|
// overwrite one. Still, use a conservative safety factor of 2.
|
|
|
|
|
|
|
|
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) |
|
|
|
|
|
|
|
return state.Error("out of disk space"); |
|
|
|
|
|
|
|
// Flush the chainstate (which may refer to block index entries).
|
|
|
|
|
|
|
|
if (!pcoinsTip->Flush()) |
|
|
|
|
|
|
|
return AbortNode(state, "Failed to write to coin database"); |
|
|
|
|
|
|
|
nLastFlush = nNow; |
|
|
|
} |
|
|
|
} |
|
|
|
// Finally remove any pruned files
|
|
|
|
|
|
|
|
if (fFlushForPrune) |
|
|
|
|
|
|
|
UnlinkPrunedFiles(setFilesToPrune); |
|
|
|
|
|
|
|
nLastWrite = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
|
|
|
|
|
|
|
|
if (fDoFullFlush) { |
|
|
|
|
|
|
|
// Typical Coin structures on disk are around 48 bytes in size.
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// an overestimation, as most will delete an existing entry or
|
|
|
|
|
|
|
|
// overwrite one. Still, use a conservative safety factor of 2.
|
|
|
|
|
|
|
|
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) |
|
|
|
|
|
|
|
return state.Error("out of disk space"); |
|
|
|
|
|
|
|
// Flush the chainstate (which may refer to block index entries).
|
|
|
|
|
|
|
|
if (!pcoinsTip->Flush()) |
|
|
|
|
|
|
|
return AbortNode(state, "Failed to write to coin database"); |
|
|
|
|
|
|
|
nLastFlush = nNow; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000)) { |
|
|
|
if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > 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).
|
|
|
|