Merge bitcoin/bitcoin#35200: node: smooth oversized dbcache warnings

d164a04342 node: smooth oversized `-dbcache` warnings (Lőrinc)

Pull request description:

  **Problem:** The oversized `-dbcache` warning threshold has a sharp formula cliff when detected RAM crosses the cutoff used by the warning logic.

  This was reported during review of [#34641](https://github.com/bitcoin/bitcoin/pull/34641#discussion_r2900769756), where an earlier version could jump from the auto default at `4095 MiB` RAM to `75%` of RAM at `4096 MiB`.
  That made `1 MiB` of extra detected RAM raise the warning threshold from about `511 MiB` to `3072 MiB`.

  The surviving warning-only code has the same shape at a different boundary.
  Below `2 GiB` RAM the cap is `DEFAULT_DB_CACHE` (`450 MiB`), but at `2 GiB` it switches to `75%` of total RAM, so a tiny increase in detected RAM can suddenly raise the warning threshold from `450 MiB` to about `1536 MiB`.

  <img width="1484" height="881" alt="Image" src="https://github.com/user-attachments/assets/b51d5d24-31b8-4a2a-8f70-e7536481f855" />

  **Fix:** Base the warning on a reserved non-dbcache memory budget instead:

  ```math
  \text{warn if } \mathit{dbcache} > \max\left(\mathit{DEFAULT\_DB\_CACHE}, 0.75 \cdot \max(\text{total RAM} - \mathit{DBCACHE\_WARNING\_RESERVED\_RAM}, 0)\right)
  ```

  `DBCACHE_WARNING_RESERVED_RAM` is `2 GiB`, so the fixed `DEFAULT_DB_CACHE` cap remains the floor below that reserve and the warning threshold grows monotonically above it.

  This keeps the warning conservative around low-memory boundaries and avoids treating a boundary-crossing `1 MiB` RAM difference as a reason to allow a much larger explicit `-dbcache`.

  **Quick reference:**

  | System RAM | Previous warning cap | New warning cap |
  | ---------- | -------------------- | --------------- |
  | 1 GiB      | 450 MiB              | 450 MiB         |
  | 2 GiB      | 1536 MiB             | 450 MiB         |
  | 3 GiB      | 2304 MiB             | 768 MiB         |
  | 4 GiB      | 3072 MiB             | 1536 MiB        |
  | 8 GiB      | 6144 MiB             | 4608 MiB        |
  | 16 GiB     | 12288 MiB            | 10752 MiB       |
  | 32 GiB     | 24576 MiB            | 23040 MiB       |

  On 32-bit builds, effective `-dbcache` values are still capped to `1024 MiB` before the warning check, so thresholds above that cap are not reachable there.

ACKs for top commit:
  optout21:
    reACK d164a04342
  sedited:
    Re-ACK d164a04342
  w0xlt:
    reACK d164a04342

Tree-SHA512: deb81f0e192261f01dda6f1575a7b2e147f1e07e813d0833c7aeaf6a3fbd7594c0145ec1c98ded74efee9005108d229942949279c9458cbec4046913387db845
This commit is contained in:
merge-script
2026-07-14 09:54:15 +02:00
2 changed files with 24 additions and 22 deletions

View File

@@ -8,6 +8,7 @@
#include <kernel/caches.h>
#include <util/byte_units.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
@@ -17,6 +18,8 @@ class ArgsManager;
static constexpr uint64_t MIN_DB_CACHE{4_MiB};
//! -dbcache default (bytes)
static constexpr uint64_t DEFAULT_DB_CACHE{DEFAULT_KERNEL_CACHE};
//! Reserved non-dbcache memory usage.
static constexpr uint64_t DBCACHE_WARNING_RESERVED_RAM{2_GiB};
namespace node {
uint64_t GetDefaultDBCache();
@@ -32,7 +35,8 @@ struct CacheSizes {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
constexpr bool ShouldWarnOversizedDbCache(uint64_t dbcache, uint64_t total_ram) noexcept
{
const uint64_t cap{(total_ram < 2_GiB) ? DEFAULT_DB_CACHE : (total_ram / 100) * 75};
const uint64_t available_ram{total_ram > DBCACHE_WARNING_RESERVED_RAM ? total_ram - DBCACHE_WARNING_RESERVED_RAM : 0};
const uint64_t cap{std::max<uint64_t>(DEFAULT_DB_CACHE, (available_ram / 4) * 3)};
return dbcache > cap;
}

View File

@@ -7,36 +7,34 @@
#include <boost/test/unit_test.hpp>
#include <cstdint>
using namespace node;
namespace {
void CheckDbCacheWarnThreshold(uint64_t threshold, uint64_t total_ram)
{
BOOST_CHECK(!ShouldWarnOversizedDbCache(threshold, total_ram));
BOOST_CHECK( ShouldWarnOversizedDbCache(threshold + 1, total_ram));
}
} // namespace
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=*/1_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/512_MiB, /*total_ram=*/1_GiB)); // At cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/1_GiB)); // Over cap
BOOST_CHECK(!ShouldWarnOversizedDbCache(MIN_DB_CACHE, 1_GiB));
// 2 GiB RAM - cap is 75%
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/1500_MiB, /*total_ram=*/2_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/1600_MiB, /*total_ram=*/2_GiB)); // Over cap
// Below DBCACHE_WARNING_RESERVED_RAM the existing fixed default dominates.
CheckDbCacheWarnThreshold(DEFAULT_DB_CACHE, 1_GiB);
CheckDbCacheWarnThreshold(DEFAULT_DB_CACHE, DBCACHE_WARNING_RESERVED_RAM);
// 4 GiB RAM - cap is 75%
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/2500_MiB, /*total_ram=*/4_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/3500_MiB, /*total_ram=*/4_GiB)); // Over cap
// Above DBCACHE_WARNING_RESERVED_RAM the warning fires at 75% of the headroom.
CheckDbCacheWarnThreshold(((3_GiB - DBCACHE_WARNING_RESERVED_RAM) / 4) * 3, 3_GiB);
// 8 GiB RAM - cap is 75%
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/6000_MiB, /*total_ram=*/8_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/7000_MiB, /*total_ram=*/8_GiB)); // Over cap
// 16 GiB RAM - cap is 75%
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/10_GiB, /*total_ram=*/16_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/15_GiB, /*total_ram=*/16_GiB)); // Over cap
// 32 GiB RAM - cap is 75%
BOOST_CHECK(!ShouldWarnOversizedDbCache(/*dbcache=*/20_GiB, /*total_ram=*/32_GiB)); // Under cap
BOOST_CHECK( ShouldWarnOversizedDbCache(/*dbcache=*/30_GiB, /*total_ram=*/32_GiB)); // Over cap
for (const auto total_ram : {8_GiB, 16_GiB, 32_GiB}) {
CheckDbCacheWarnThreshold(((total_ram - DBCACHE_WARNING_RESERVED_RAM) / 4) * 3, total_ram);
}
}
BOOST_AUTO_TEST_SUITE_END()