coins: test chainstate flush baseline

Add `CDBWrapper::GetProperty()` and expose it through `CCoinsViewDB::GetDBProperty()` so coins tests can inspect LevelDB runtime properties through the coins view.
Use it in a coins DB flush baseline that records the LevelDB layout after flushing while keeping readback coverage for the flushed coin and best block.

Co-authored-by: Andrew Toth <andrewstoth@gmail.com>
This commit is contained in:
Lőrinc
2026-06-04 19:42:00 +02:00
parent c117bbc467
commit b10889d107
5 changed files with 42 additions and 2 deletions

View File

@@ -301,11 +301,16 @@ void CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
}
}
std::optional<std::string> CDBWrapper::GetProperty(const std::string& property) const
{
if (std::string value; DBContext().pdb->GetProperty(property, &value)) return value;
return std::nullopt;
}
size_t CDBWrapper::DynamicMemoryUsage() const
{
std::string memory;
std::optional<size_t> parsed;
if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
if (auto memory{GetProperty("leveldb.approximate-memory-usage")}; !memory || !(parsed = ToIntegral<size_t>(*memory))) {
LogDebug(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
return 0;
}

View File

@@ -258,6 +258,9 @@ public:
void WriteBatch(CDBBatch& batch, bool fSync = false);
//! Return a LevelDB property value, if available.
std::optional<std::string> GetProperty(const std::string& property) const;
// Get an estimate of LevelDB memory usage (in bytes).
size_t DynamicMemoryUsage() const;

View File

@@ -14,6 +14,7 @@
#include <uint256.h>
#include <undo.h>
#include <util/byte_units.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <map>
@@ -1060,6 +1061,28 @@ BOOST_FIXTURE_TEST_CASE(ccoins_flush_behavior, FlushTest)
}
}
BOOST_FIXTURE_TEST_CASE(coins_db_leveldb_layout, FlushTest)
{
auto level2_files{[](CCoinsViewDB& base) {
return *Assert(ToIntegral<int>(*Assert(base.GetDBProperty("leveldb.num-files-at-level2"))));
}};
const COutPoint outpoint{Txid::FromUint256(m_rng.rand256()), 0};
const Coin coin{MakeCoin()};
const uint256 block_hash{m_rng.rand256()};
CCoinsViewDB base{{.path = m_args.GetDataDirBase() / "coins_db_leveldb_layout", .cache_bytes = 1_MiB, .wipe_data = true}, {}};
CCoinsViewCache cache{&base};
cache.EmplaceCoinInternalDANGER(COutPoint{outpoint}, Coin{coin});
cache.SetBestBlock(block_hash);
cache.Sync();
BOOST_CHECK_EQUAL(level2_files(base), 0);
BOOST_CHECK(*Assert(base.GetCoin(outpoint)) == coin);
BOOST_CHECK_EQUAL(base.GetBestBlock(), block_hash);
}
BOOST_AUTO_TEST_CASE(coins_resource_is_used)
{
CCoinsMapMemoryResource resource;

View File

@@ -175,6 +175,11 @@ size_t CCoinsViewDB::EstimateSize() const
return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
}
std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)
{
return m_db->GetProperty(property);
}
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
class CCoinsViewDBCursor: public CCoinsViewCursor
{

View File

@@ -17,6 +17,7 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
class COutPoint;
@@ -54,6 +55,9 @@ public:
//! Dynamically alter the underlying leveldb cache size.
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
//! Return an underlying LevelDB property value, if available.
std::optional<std::string> GetDBProperty(const std::string& property);
};
#endif // BITCOIN_TXDB_H