mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-06 21:19:35 +01:00
Merge bitcoin/bitcoin#33512: coins: use dirty entry count for flush warnings and disk space checks
afb1bc120evalidation: Use dirty entry count in flush warnings and disk space checks (Pieter Wuille)b413491a1ccoins: Keep track of number of dirty entries in `CCoinsViewCache` (Pieter Wuille)7e52b1b945fuzz: call `EmplaceCoinInternalDANGER` as well in `SimulationTest` (Lőrinc) Pull request description: ### Problem Now that non-wiping flushes are possible (#28280, #28233), the cache may be mostly clean at flush time. But the flush warning, disk-space check, and benchmark logging still used total cache size, so a node with a 10 GiB cache that only needs to write a small fraction of dirty entries could still trigger a scary warning via the disk-space checks. The previous `DynamicMemoryUsage` metric was also fundamentally wrong for estimating disk writes, even before non-wiping flushes. In-memory coin size differs from on-disk write size due to LevelDB overhead, log doubling, and compaction. The warning also only fired in `FlushStateToDisk`, so `AssumeUTXO` snapshot loads never warned at all. ### Fix This PR tracks the actual number of dirty entries via `m_dirty_count` in `CCoinsViewCache`, maintained alongside the existing dirty-flag linked list, `SanityCheck` cross-validating both counts. The warning and benchmark log move from `FlushStateToDisk` down to `CCoinsViewDB::BatchWrite`, where the actual I/O happens. This is the single place all flush paths converge (regular flushes, syncs, and snapshot loads), so the warning now fires correctly for `AssumeUTXO` too. The threshold changes from 1 GiB of memory to 10 million dirty entries, which is roughly equivalent but avoids the in-memory vs on-disk size confusion. The disk-space safety check now uses `GetDirtyCount()` with the existing conservative 48-byte-per-entry estimate, preventing unnecessary shutdowns when the cache is large but mostly clean. --- Note: the first commit adds fuzz coverage for `EmplaceCoinInternalDANGER` in `SimulationTest` to exercise the accounting paths before modifying them. Note: this is a revival of #31703 with all outstanding review feedback addressed. ACKs for top commit: Eunovo: Concept ACKafb1bc120eandrewtoth: re-ACKafb1bc120esipa: Code review ACKafb1bc120esedited: ACKafb1bc120eTree-SHA512: 4133c6669fd20836ae2fb62ed804cdf6ebaa61076927b54fc412e42455a2f0d4cadfab0844064f9c32431eacb1f5e47b78de8e5cde1b26ba7239a7becf92f369
This commit is contained in:
@@ -89,8 +89,6 @@ using node::CBlockIndexHeightOnlyComparator;
|
||||
using node::CBlockIndexWorkComparator;
|
||||
using node::SnapshotMetadata;
|
||||
|
||||
/** Size threshold for warning about slow UTXO set flush to disk. */
|
||||
static constexpr size_t WARN_FLUSH_COINS_SIZE = 1 << 30; // 1 GiB
|
||||
/** Time window to wait between writing blocks/block index and chainstate to disk.
|
||||
* Randomize writing time inside the window to prevent a situation where the
|
||||
* network over time settles into a few cohorts of synchronized writers.
|
||||
@@ -2708,8 +2706,8 @@ bool Chainstate::FlushStateToDisk(
|
||||
std::set<int> setFilesToPrune;
|
||||
bool full_flush_completed = false;
|
||||
|
||||
const size_t coins_count = CoinsTip().GetCacheSize();
|
||||
const size_t coins_mem_usage = CoinsTip().DynamicMemoryUsage();
|
||||
[[maybe_unused]] const size_t coins_count{CoinsTip().GetCacheSize()};
|
||||
[[maybe_unused]] const size_t coins_mem_usage{CoinsTip().DynamicMemoryUsage()};
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -2802,16 +2800,12 @@ bool Chainstate::FlushStateToDisk(
|
||||
}
|
||||
|
||||
if (!CoinsTip().GetBestBlock().IsNull()) {
|
||||
if (coins_mem_usage >= WARN_FLUSH_COINS_SIZE) LogWarning("Flushing large (%d GiB) UTXO set to disk, it may take several minutes", coins_mem_usage >> 30);
|
||||
LOG_TIME_MILLIS_WITH_CATEGORY(strprintf("write coins cache to disk (%d coins, %.2fKiB)",
|
||||
coins_count, coins_mem_usage >> 10), BCLog::BENCH);
|
||||
|
||||
// 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(m_chainman.m_options.datadir, 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
|
||||
if (!CheckDiskSpace(m_chainman.m_options.datadir, 48 * 2 * 2 * CoinsTip().GetDirtyCount())) {
|
||||
return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));
|
||||
}
|
||||
// Flush the chainstate (which may refer to block index entries).
|
||||
|
||||
Reference in New Issue
Block a user