mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
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> Github-Pull: #35465 Rebased-From: 394e473d42ba1383dfec45a3eafa8a73a09dbe8b
This commit is contained in:
parent
9dd8e32853
commit
1907b3f56c
4 changed files with 43 additions and 10 deletions
|
|
@ -1067,7 +1067,7 @@ BOOST_FIXTURE_TEST_CASE(coins_db_leveldb_layout, FlushTest)
|
|||
cache.Sync();
|
||||
|
||||
BOOST_CHECK_EQUAL(level2_files(base), 0);
|
||||
WITH_LOCK(::cs_main, base.CompactFull());
|
||||
WITH_LOCK(::cs_main, return base.CompactFull()).wait();
|
||||
BOOST_CHECK_EQUAL(level2_files(base), 1);
|
||||
|
||||
BOOST_CHECK(*Assert(base.GetCoin(outpoint)) == coin);
|
||||
|
|
|
|||
34
src/txdb.cpp
34
src/txdb.cpp
|
|
@ -12,10 +12,14 @@
|
|||
#include <random.h>
|
||||
#include <serialize.h>
|
||||
#include <uint256.h>
|
||||
#include <util/threadnames.h>
|
||||
#include <util/vector.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
|
|
@ -51,11 +55,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();
|
||||
|
|
@ -161,12 +176,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 */
|
||||
|
|
|
|||
11
src/txdb.h
11
src/txdb.h
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
|
@ -40,9 +41,13 @@ class CCoinsViewDB final : public CCoinsView
|
|||
protected:
|
||||
DBParams m_db_params;
|
||||
CoinsViewOptions m_options;
|
||||
//! Prevents CompactFull() from using m_db while ResizeCache() replaces it.
|
||||
Mutex m_db_mutex;
|
||||
std::unique_ptr<CDBWrapper> m_db;
|
||||
std::shared_future<void> m_compaction;
|
||||
public:
|
||||
explicit CCoinsViewDB(DBParams db_params, CoinsViewOptions options);
|
||||
~CCoinsViewDB() override;
|
||||
|
||||
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
|
||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||
|
|
@ -56,13 +61,13 @@ public:
|
|||
size_t EstimateSize() const override;
|
||||
|
||||
//! Dynamically alter the underlying leveldb cache size.
|
||||
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_db_mutex);
|
||||
|
||||
//! @returns filesystem path to on-disk storage or std::nullopt if in memory.
|
||||
std::optional<fs::path> StoragePath() { return m_db->StoragePath(); }
|
||||
|
||||
//! Perform a blocking full compaction of the underlying LevelDB.
|
||||
void CompactFull() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
//! Perform a full compaction of the underlying LevelDB on a one-shot background thread.
|
||||
std::shared_future<void> CompactFull() EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_db_mutex);
|
||||
|
||||
//! Return an underlying LevelDB property value, if available.
|
||||
std::optional<std::string> GetDBProperty(const std::string& property);
|
||||
|
|
|
|||
|
|
@ -2952,9 +2952,11 @@ bool Chainstate::FlushStateToDisk(
|
|||
m_chainman.m_options.signals->ChainStateFlushed(this->GetRole(), m_chain.GetLocator());
|
||||
}
|
||||
|
||||
if (!m_chainman.m_interrupt && m_chainman.GetAll().size() == 1) { // Skip AssumeUTXO
|
||||
if (ShouldCompactChainstate(m_chainman.IsInitialBlockDownload())) {
|
||||
if (!m_chainman.m_interrupt && ShouldCompactChainstate(m_chainman.IsInitialBlockDownload())) {
|
||||
try {
|
||||
CoinsDB().CompactFull();
|
||||
} catch (const std::exception& e) {
|
||||
LogWarning("Failed to start chainstate compaction (%s)", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue