diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index eb222078b5e..7aa5ba3b4ba 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -291,11 +291,16 @@ void CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) } } +std::optional 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 parsed; - if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral(memory))) { + if (auto memory{GetProperty("leveldb.approximate-memory-usage")}; !memory || !(parsed = ToIntegral(*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 2eee6c1c023..18e152f112c 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -250,6 +250,9 @@ public: void WriteBatch(CDBBatch& batch, bool fSync = false); + //! Return a LevelDB property value, if available. + std::optional 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 8321bf6a18c..79b61c97aaa 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -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(*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 2c39cf2767b..3c8eb575ad9 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -168,6 +168,11 @@ size_t CCoinsViewDB::EstimateSize() const return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1)); } +std::optional 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 248fe43e559..4f8cb873cb9 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -17,6 +17,7 @@ #include #include #include +#include #include class COutPoint; @@ -53,6 +54,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 GetDBProperty(const std::string& property); }; #endif // BITCOIN_TXDB_H