init: add utxo snapshot detection

Add functionality for activating a snapshot-based chainstate if one is
detected on-disk.

Also cautiously initialize chainstate cache usages so that we don't
somehow blow past our cache allowances during initialization, then
rebalance at the end of init.

Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
This commit is contained in:
James O'Beirne
2022-04-20 17:40:01 -04:00
parent f9f1735f13
commit 252abd1e8b
7 changed files with 87 additions and 34 deletions

View File

@@ -48,10 +48,15 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
}
LOCK(cs_main);
chainman.InitializeChainstate(options.mempool);
chainman.m_total_coinstip_cache = cache_sizes.coins;
chainman.m_total_coinsdb_cache = cache_sizes.coins_db;
// Load the fully validated chainstate.
chainman.InitializeChainstate(options.mempool);
// Load a chain created from a UTXO snapshot, if any exist.
chainman.DetectSnapshotChainstate(options.mempool);
auto& pblocktree{chainman.m_blockman.m_block_tree_db};
// new CBlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
@@ -98,12 +103,20 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
}
// Conservative value which is arbitrarily chosen, as it will ultimately be changed
// by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure
// that the sum of the two caches (40%) does not exceed the allowable amount
// during this temporary initialization state.
double init_cache_fraction = 0.2;
// At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()!
for (Chainstate* chainstate : chainman.GetAll()) {
LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
chainstate->InitCoinsDB(
/*cache_size_bytes=*/cache_sizes.coins_db,
/*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
/*in_memory=*/options.coins_db_in_memory,
/*should_wipe=*/options.reindex || options.reindex_chainstate);
@@ -125,7 +138,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
}
// The on-disk coinsdb is now in a good state, create the cache
chainstate->InitCoinsCache(cache_sizes.coins);
chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
assert(chainstate->CanFlushToDisk());
if (!is_coinsview_empty(chainstate)) {
@@ -146,6 +159,11 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
};
}
// Now that chainstates are loaded and we're able to flush to
// disk, rebalance the coins caches to desired levels based
// on the condition of each chainstate.
chainman.MaybeRebalanceCaches();
return {ChainstateLoadStatus::SUCCESS, {}};
}

View File

@@ -8,6 +8,7 @@
#include <logging.h>
#include <streams.h>
#include <uint256.h>
#include <util/system.h>
#include <validation.h>
#include <cstdio>
@@ -76,4 +77,15 @@ std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
return base_blockhash;
}
std::optional<fs::path> FindSnapshotChainstateDir()
{
fs::path possible_dir =
gArgs.GetDataDirNet() / fs::u8path(strprintf("chainstate%s", SNAPSHOT_CHAINSTATE_SUFFIX));
if (fs::exists(possible_dir)) {
return possible_dir;
}
return std::nullopt;
}
} // namespace node

View File

@@ -58,8 +58,14 @@ bool WriteSnapshotBaseBlockhash(Chainstate& snapshot_chainstate)
std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Suffix appended to the chainstate (leveldb) dir when created based upon
//! a snapshot.
constexpr std::string_view SNAPSHOT_CHAINSTATE_SUFFIX = "_snapshot";
//! Return a path to the snapshot-based chainstate dir, if one exists.
std::optional<fs::path> FindSnapshotChainstateDir();
} // namespace node
#endif // BITCOIN_NODE_UTXO_SNAPSHOT_H