coins: test chainstate flush baseline
What changed, and why it matters
This commit adds a new test helper that lets the existing test suite read internal LevelDB statistics, and uses it to add a baseline test checking how many files LevelDB creates after flushing a small coins database. It is purely a testing/instrumentation change and does not alter production behavior.
No security action needed; review as normal test-only refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch exposes CDBWrapper::GetProperty() and forwards it through CCoinsViewDB::GetDBProperty() so that coins_tests.cpp can query LevelDB runtime properties. It refactors DynamicMemoryUsage() to use the new helper, and adds a BOOST_FIXTURE_TEST_CASE named coins_db_leveldb_layout that writes a single coin and best-block to a fresh CCoinsViewDB, flushes, asserts leveldb.num-files-at-level2 is 0, and verifies readback. No consensus, P2P, wallet, or RPC code is changed.
Changed components
src/dbwrapper.cppsrc/dbwrapper.hsrc/txdb.cppsrc/txdb.hsrc/test/coins_tests.cppInspect captured patch +42 / −2
diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp
index 1d2b6f0d..d51a347c 100644
--- a/src/dbwrapper.cpp
+++ b/src/dbwrapper.cpp
@@ -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;
}
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index edbe69ce..a5f63116 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -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;
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 14ccb1c4..847297e5 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -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;
diff --git a/src/txdb.cpp b/src/txdb.cpp
index a41dfd16..7f45a7c0 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -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
{
diff --git a/src/txdb.h b/src/txdb.h
index b19b312a..827b36f3 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -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
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.