Skip to content

Commit ef15b5f

Browse files
committed
Write block index more frequently than cache flushes
1 parent b0260ee commit ef15b5f

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

src/main.cpp

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,6 +1876,8 @@ enum FlushStateMode {
18761876
bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
18771877
LOCK2(cs_main, cs_LastBlockFile);
18781878
static int64_t nLastWrite = 0;
1879+
static int64_t nLastFlush = 0;
1880+
static int64_t nLastSetChain = 0;
18791881
std::set<int> setFilesToPrune;
18801882
bool fFlushForPrune = false;
18811883
try {
@@ -1889,16 +1891,36 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
18891891
}
18901892
}
18911893
}
1892-
if ((mode == FLUSH_STATE_ALWAYS) ||
1893-
((mode == FLUSH_STATE_PERIODIC || mode == FLUSH_STATE_IF_NEEDED) && pcoinsTip->DynamicMemoryUsage() > nCoinCacheUsage) ||
1894-
(mode == FLUSH_STATE_PERIODIC && GetTimeMicros() > nLastWrite + DATABASE_WRITE_INTERVAL * 1000000) ||
1895-
fFlushForPrune) {
1896-
// Typical CCoins structures on disk are around 100 bytes in size.
1894+
int64_t nNow = GetTimeMicros();
1895+
// Avoid writing/flushing immediately after startup.
1896+
if (nLastWrite == 0) {
1897+
nLastWrite = nNow;
1898+
}
1899+
if (nLastFlush == 0) {
1900+
nLastFlush = nNow;
1901+
}
1902+
if (nLastSetChain == 0) {
1903+
nLastSetChain = nNow;
1904+
}
1905+
size_t cacheSize = pcoinsTip->DynamicMemoryUsage();
1906+
// The cache is large and close to the limit, but we have time now (not in the middle of a block processing).
1907+
bool fCacheLarge = mode == FLUSH_STATE_PERIODIC && cacheSize * (10.0/9) > nCoinCacheUsage;
1908+
// The cache is over the limit, we have to write now.
1909+
bool fCacheCritical = mode == FLUSH_STATE_IF_NEEDED && cacheSize > nCoinCacheUsage;
1910+
// 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.
1911+
bool fPeriodicWrite = mode == FLUSH_STATE_PERIODIC && nNow > nLastWrite + (int64_t)DATABASE_WRITE_INTERVAL * 1000000;
1912+
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
1913+
bool fPeriodicFlush = mode == FLUSH_STATE_PERIODIC && nNow > nLastFlush + (int64_t)DATABASE_FLUSH_INTERVAL * 1000000;
1914+
// Combine all conditions that result in a full cache flush.
1915+
bool fDoFullFlush = (mode == FLUSH_STATE_ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush || fFlushForPrune;
1916+
// Write blocks and block index to disk.
1917+
if (fDoFullFlush || fPeriodicWrite) {
1918+
// Typical CCoins structures on disk are around 128 bytes in size.
18971919
// Pushing a new one to the database can cause it to be written
18981920
// twice (once in the log, and once in the tables). This is already
18991921
// an overestimation, as most will delete an existing entry or
19001922
// overwrite one. Still, use a conservative safety factor of 2.
1901-
if (!CheckDiskSpace(100 * 2 * 2 * pcoinsTip->GetCacheSize()))
1923+
if (fDoFullFlush && !CheckDiskSpace(128 * 2 * 2 * pcoinsTip->GetCacheSize()))
19021924
return state.Error("out of disk space");
19031925
// First make sure all block and undo data is flushed to disk.
19041926
FlushBlockFile();
@@ -1920,21 +1942,24 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) {
19201942
return state.Abort("Files to write to block index database");
19211943
}
19221944
}
1923-
// Flush the chainstate (which may refer to block index entries).
1924-
if (!pcoinsTip->Flush())
1925-
return state.Abort("Failed to write to coin database");
1926-
19271945
// Finally remove any pruned files
19281946
if (fFlushForPrune) {
19291947
UnlinkPrunedFiles(setFilesToPrune);
19301948
fCheckForPruning = false;
19311949
}
1932-
1950+
nLastWrite = nNow;
1951+
}
1952+
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
1953+
if (fDoFullFlush) {
1954+
// Flush the chainstate (which may refer to block index entries).
1955+
if (!pcoinsTip->Flush())
1956+
return state.Abort("Failed to write to coin database");
1957+
nLastFlush = nNow;
1958+
}
1959+
if ((mode == FLUSH_STATE_ALWAYS || mode == FLUSH_STATE_PERIODIC) && nNow > nLastSetChain + (int64_t)DATABASE_WRITE_INTERVAL * 1000000) {
19331960
// Update best block in wallet (so we can detect restored wallets).
1934-
if (mode != FLUSH_STATE_IF_NEEDED) {
1935-
GetMainSignals().SetBestChain(chainActive.GetLocator());
1936-
}
1937-
nLastWrite = GetTimeMicros();
1961+
GetMainSignals().SetBestChain(chainActive.GetLocator());
1962+
nLastSetChain = nNow;
19381963
}
19391964
} catch (const std::runtime_error& e) {
19401965
return state.Abort(std::string("System error while flushing: ") + e.what());

src/main.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000;
8282
* degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning
8383
* harder). We'll probably want to make this a per-peer adaptive value at some point. */
8484
static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024;
85-
/** Time to wait (in seconds) between writing blockchain state to disk. */
86-
static const unsigned int DATABASE_WRITE_INTERVAL = 3600;
85+
/** Time to wait (in seconds) between writing blocks/block index to disk. */
86+
static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60;
87+
/** Time to wait (in seconds) between flushing chainstate to disk. */
88+
static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60;
8789
/** Maximum length of reject messages. */
8890
static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111;
8991

0 commit comments

Comments
 (0)