What changed, and why it matters
This change adds a startup warning when a user sets the Bitcoin Core database cache (-dbcache) to a value that the software considers too large for the system's total RAM. It does not change the configured value or prevent the node from running; it only prints a warning. The goal is to help users avoid accidentally making their computer slow or unresponsive due to heavy memory use or swapping.
No security action required. This is a usability improvement. Operators may review -dbcache settings when the warning appears, but the change does not fix or introduce a vulnerability.
Security signals we found
Informational startup warning only; no enforcement or value clamping
Refactors -dbcache parsing into reusable CalculateDbCacheBytes()
Adds ShouldWarnOversizedDbCache() threshold logic
Adds unit tests covering threshold boundaries
Evidence from the diff
The commit introduces LogOversizedDbCache() and ShouldWarnOversizedDbCache() in src/node/caches.cpp/h. It refactors cache parsing into CalculateDbCacheBytes() and, during AppInitMain(), emits an InitWarning if the configured -dbcache exceeds a cap: DEFAULT_DB_CACHE (450 MiB) when total RAM is below 2 GiB, otherwise 75% of total RAM. The warning is purely informational; the actual cache size is unchanged. Unit tests for the warning threshold are added in src/test/caches_tests.cpp.
Changed components
src/init.cppsrc/node/caches.cppsrc/node/caches.hsrc/test/caches_tests.cppInspect captured patch +73 / −6
diff --git a/src/init.cpp b/src/init.cpp
index 4ff57a34..2f518eba 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1767,6 +1767,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 7: load block chain
// cache size calculations
+ node::LogOversizedDbCache(args);
const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
LogInfo("Cache configuration:");
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index d5d69fc2..e4a5e6d1 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -5,9 +5,12 @@
#include <node/caches.h>
#include <common/args.h>
+#include <common/system.h>
#include <index/txindex.h>
#include <kernel/caches.h>
#include <logging.h>
+#include <node/interface_ui.h>
+#include <tinyformat.h>
#include <util/byte_units.h>
#include <algorithm>
@@ -23,16 +26,20 @@ static constexpr size_t MAX_FILTER_INDEX_CACHE{1024_MiB};
static constexpr size_t MAX_32BIT_DBCACHE{1024_MiB};
namespace node {
-CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
+size_t CalculateDbCacheBytes(const ArgsManager& args)
{
- // Convert -dbcache from MiB units to bytes. The total cache is floored by MIN_DB_CACHE and capped by max size_t value.
- size_t total_cache{DEFAULT_DB_CACHE};
- if (std::optional<int64_t> db_cache = args.GetIntArg("-dbcache")) {
+ if (auto db_cache{args.GetIntArg("-dbcache")}) {
if (*db_cache < 0) db_cache = 0;
- uint64_t db_cache_bytes = SaturatingLeftShift<uint64_t>(*db_cache, 20);
+ const uint64_t db_cache_bytes{SaturatingLeftShift<uint64_t>(*db_cache, 20)};
constexpr auto max_db_cache{sizeof(void*) == 4 ? MAX_32BIT_DBCACHE : std::numeric_limits<size_t>::max()};
- total_cache = std::max<size_t>(MIN_DB_CACHE, std::min<uint64_t>(db_cache_bytes, max_db_cache));
+ return std::max<size_t>(MIN_DB_CACHE, std::min<uint64_t>(db_cache_bytes, max_db_cache));
}
+ return DEFAULT_DB_CACHE;
+}
+
+CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
+{
+ size_t total_cache{CalculateDbCacheBytes(args)};
IndexCacheSizes index_sizes;
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
@@ -44,4 +51,15 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
}
return {index_sizes, kernel::CacheSizes{total_cache}};
}
+
+void LogOversizedDbCache(const ArgsManager& args) noexcept
+{
+ if (const auto total_ram{GetTotalRAM()}) {
+ const size_t db_cache{CalculateDbCacheBytes(args)};
+ if (ShouldWarnOversizedDbCache(db_cache, *total_ram)) {
+ InitWarning(bilingual_str{tfm::format(_("A %zu MiB dbcache may be too large for a system memory of only %zu MiB."),
+ db_cache >> 20, *total_ram >> 20)});
+ }
+ }
+}
} // namespace node
diff --git a/src/node/caches.h b/src/node/caches.h
index f24e9cc9..c0ef41ec 100644
--- a/src/node/caches.h
+++ b/src/node/caches.h
@@ -27,6 +27,13 @@ struct CacheSizes {
kernel::CacheSizes kernel;
};
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
+constexpr bool ShouldWarnOversizedDbCache(size_t dbcache, size_t total_ram) noexcept
+{
+ const size_t cap{(total_ram < 2048_MiB) ? DEFAULT_DB_CACHE : (total_ram / 100) * 75};
+ return dbcache > cap;
+}
+
+void LogOversizedDbCache(const ArgsManager& args) noexcept;
} // namespace node
#endif // BITCOIN_NODE_CACHES_H
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index a818dba7..c963afff 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -25,6 +25,7 @@ add_executable(test_bitcoin
blockmanager_tests.cpp
bloom_tests.cpp
bswap_tests.cpp
+ caches_tests.cpp
chainstate_write_tests.cpp
checkqueue_tests.cpp
cluster_linearize_tests.cpp
diff --git a/src/test/caches_tests.cpp b/src/test/caches_tests.cpp
new file mode 100644
index 00000000..3086118f
--- /dev/null
+++ b/src/test/caches_tests.cpp
@@ -0,0 +1,40 @@
+#include <node/caches.h>
+#include <util/byte_units.h>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace node;
+
+BOOST_AUTO_TEST_SUITE(caches_tests)
+
+BOOST_AUTO_TEST_CASE(oversized_dbcache_warning)
+{
+ // memory restricted setup - cap is DEFAULT_DB_CACHE (450 MiB)
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/4_MiB, /*total_ram=*/1024_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/512_MiB, /*total_ram=*/1024_MiB)); // At cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/1024_MiB)); // Over cap
+
+ // 2 GiB RAM - cap is 75%
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/2048_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1600_MiB, /*total_ram=*/2048_MiB)); // Over cap
+
+ if constexpr (SIZE_MAX == UINT64_MAX) {
+ // 4 GiB RAM - cap is 75%
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/2500_MiB, /*total_ram=*/4096_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/3500_MiB, /*total_ram=*/4096_MiB)); // Over cap
+
+ // 8 GiB RAM - cap is 75%
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/6000_MiB, /*total_ram=*/8192_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/7000_MiB, /*total_ram=*/8192_MiB)); // Over cap
+
+ // 16 GiB RAM - cap is 75%
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/10'000_MiB, /*total_ram=*/16384_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/15'000_MiB, /*total_ram=*/16384_MiB)); // Over cap
+
+ // 32 GiB RAM - cap is 75%
+ BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/20'000_MiB, /*total_ram=*/32768_MiB)); // Under cap
+ BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/30'000_MiB, /*total_ram=*/32768_MiB)); // Over cap
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
Why this scored 21/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.