Skip to content
This repository has been archived by the owner on Feb 1, 2021. It is now read-only.

Commit

Permalink
rework validation code, fix rescan failures caused by disconnect failure
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg-Griffith committed May 22, 2019
1 parent acfadfe commit f68cebf
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 220 deletions.
165 changes: 89 additions & 76 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1124,97 +1124,110 @@ void static FlushBlockFile(bool fFinalize = false)
*/
bool FlushStateToDisk(CValidationState &state, FlushStateMode mode)
{
LOCK2(cs_main, cs_LastBlockFile);
static int64_t nLastWrite = 0;
static int64_t nLastFlush = 0;
static int64_t nLastSetChain = 0;
try
{
int64_t nNow = GetTimeMicros();
// Avoid writing/flushing immediately after startup.
if (nLastWrite == 0)
{
nLastWrite = nNow;
}
if (nLastFlush == 0)
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;
}
size_t cacheSize = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage();
static int64_t nSizeAfterLastFlush = 0;
// The cache is close to the limit. Try to flush and trim.
bool fCacheCritical = ((mode == FLUSH_STATE_IF_NEEDED) && (cacheSize > nCoinCacheUsage * 0.995)) ||
(cacheSize - nSizeAfterLastFlush > (int64_t)nMaxCacheIncreaseSinceLastFlush);
// 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) || fCacheCritical || fPeriodicFlush;
// Write blocks and block index to disk.
if (fDoFullFlush || fPeriodicWrite)
{
// Depend on nMinDiskSpace to ensure we can write block index
if (!CheckDiskSpace(0))
{
nLastFlush = nNow;
return state.Error("out of disk space");
}
if (nLastSetChain == 0)
FlushBlockFile();
// Then update all block file information (which may refer to block and undo files).
{
nLastSetChain = nNow;
}
size_t cacheSize = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage();
// The cache is large and close to the limit, but we have time now (not in the middle of a block processing).
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0 / 9) > nCoinCacheUsage;
// The cache is over the limit, we have to write now.
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage;
// 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;
// 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();)
{
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 (!pnetMan->getChainActive()->pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks))
{
return AbortNode(state, "Files to write to block index database");
}
vFiles.push_back(std::make_pair(*it, &vinfoBlockFile[*it]));
setDirtyFileInfo.erase(it++);
}
nLastWrite = nNow;
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 (!pnetMan->getChainActive()->pblocktree->WriteBatchSync(vFiles, nLastBlockFile, vBlocks))
{
return AbortNode(state, "Files to write to block index database");
}
}
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 * pnetMan->getChainActive()->pcoinsTip->GetCacheSize()))
{
return state.Error("out of disk space");
}
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
if (fDoFullFlush)
// Flush the chainstate (which may refer to block index entries).
if (!pnetMan->getChainActive()->pcoinsTip->Flush())
{
// Typical CCoins structures on disk are around 128 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(128 * 2 * 2 * pnetMan->getChainActive()->pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
// Flush the chainstate (which may refer to block index entries).
if (!pnetMan->getChainActive()->pcoinsTip->Flush())
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
return AbortNode(state, "Failed to write to coin database");
}
if (fDoFullFlush || ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) &&
nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000))
nLastFlush = nNow;
// Trim, but never trim more than nMaxCacheIncreaseSinceLastFlush
size_t nTrimSize = nCoinCacheUsage * .90;
if (nCoinCacheUsage - nMaxCacheIncreaseSinceLastFlush > nTrimSize)
{
// Update best block in wallet (so we can detect restored wallets).
GetMainSignals().SetBestChain(pnetMan->getChainActive()->chainActive.GetLocator());
nLastSetChain = nNow;
nTrimSize = nCoinCacheUsage - nMaxCacheIncreaseSinceLastFlush;
}
pnetMan->getChainActive()->pcoinsTip->Trim(nTrimSize);
nSizeAfterLastFlush = pnetMan->getChainActive()->pcoinsTip->DynamicMemoryUsage();
}
catch (const std::runtime_error &e)
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).
GetMainSignals().SetBestChain(pnetMan->getChainActive()->chainActive.GetLocator());
nLastSetChain = nNow;
}

// As a safeguard, periodically check and correct any drift in the value of cachedCoinsUsage. While a
// correction should never be needed, resetting the value allows the node to continue operating, and only
// an error is reported if the new and old values do not match.
if (fPeriodicFlush)
{
return AbortNode(state, std::string("System error while flushing: ") + e.what());
pnetMan->getChainActive()->pcoinsTip->ResetCachedCoinUsage();
}
return true;
}
Expand Down
Loading

0 comments on commit f68cebf

Please sign in to comment.