coins: compact chainstate in background

Full chainstate compaction can take minutes on large databases.
Move `CCoinsViewDB::CompactFull()` to a named `utxocompact` one-shot background thread so validation only schedules the work.

When validation selects compaction after a full flush, the chainstate was just written and another write is less likely to be needed immediately.
The coins view destructor waits for completion, and a mutex prevents compaction from using `m_db` while `ResizeCache()` replaces it.

Co-authored-by: Andrew Toth <andrewstoth@gmail.com>
This commit is contained in:
Lőrinc
2026-06-08 16:15:21 +02:00
parent aa021b26f3
commit 394e473d42
4 changed files with 43 additions and 10 deletions

View File

@@ -14,10 +14,14 @@
#include <uint256.h>
#include <util/byte_units.h>
#include <util/log.h>
#include <util/threadnames.h>
#include <util/vector.h>
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <exception>
#include <future>
#include <iterator>
#include <utility>
@@ -56,11 +60,22 @@ CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
m_options{std::move(options)},
m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
CCoinsViewDB::~CCoinsViewDB()
{
if (m_compaction.valid()) {
if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));
}
m_compaction.wait();
}
}
void CCoinsViewDB::ResizeCache(size_t new_cache_size)
{
// We can't do this operation with an in-memory DB since we'll lose all the coins upon
// reset.
if (!m_db_params.memory_only) {
LOCK(m_db_mutex);
// Have to do a reset first to get the original `m_db` state to release its
// filesystem lock.
m_db.reset();
@@ -180,12 +195,23 @@ std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& proper
return m_db->GetProperty(property);
}
void CCoinsViewDB::CompactFull()
std::shared_future<void> CCoinsViewDB::CompactFull()
{
AssertLockHeld(::cs_main);
LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
m_db->CompactFull();
LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;
m_compaction = std::async(std::launch::async, [this] {
try {
util::ThreadRename("utxocompact");
LOCK(m_db_mutex);
LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
m_db->CompactFull();
LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
} catch (const std::exception& e) {
LogWarning("Failed chainstate compaction (%s)", e.what());
}
}).share();
return m_compaction;
}
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */