Skip to content

Commit 554befd

Browse files
l0rincLarryRuane
andcommitted
test: revive getcoinscachesizestate
After the changes in #25325 `getcoinscachesizestate` always end the test early, see: https://maflcko.github.io/b-c-cov/test_bitcoin.coverage/src/test/validation_flush_tests.cpp.gcov.html The test revival was extracted from a related PR where it was discovered, see: #28531 (comment) Co-authored-by: Larry Ruane <[email protected]>
1 parent 64ed0fa commit 554befd

File tree

1 file changed

+38
-127
lines changed

1 file changed

+38
-127
lines changed
Lines changed: 38 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// Copyright (c) 2019-2022 The Bitcoin Core developers
1+
// Copyright (c) 2019-present The Bitcoin Core developers
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4-
//
4+
55
#include <sync.h>
66
#include <test/util/coins.h>
77
#include <test/util/random.h>
@@ -12,145 +12,56 @@
1212

1313
BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
1414

15-
//! Test utilities for detecting when we need to flush the coins cache based
16-
//! on estimated memory usage.
17-
//!
18-
//! @sa Chainstate::GetCoinsCacheSizeState()
19-
//!
15+
//! Verify that Chainstate::GetCoinsCacheSizeState() switches from OK→LARGE→CRITICAL
16+
//! at the expected utilization thresholds, first with *no* mempool head-room,
17+
//! then with additional mempool head-room.
2018
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
2119
{
2220
Chainstate& chainstate{m_node.chainman->ActiveChainstate()};
2321

24-
constexpr bool is_64_bit = sizeof(void*) == 8;
25-
2622
LOCK(::cs_main);
27-
auto& view = chainstate.CoinsTip();
28-
29-
// The number of bytes consumed by coin's heap data, i.e.
30-
// CScript (prevector<36, unsigned char>) when assigned 56 bytes of data per above.
31-
// See also: Coin::DynamicMemoryUsage().
32-
constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
33-
34-
auto print_view_mem_usage = [](CCoinsViewCache& view) {
35-
BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
36-
};
37-
38-
// PoolResource defaults to 256 KiB that will be allocated, so we'll take that and make it a bit larger.
39-
constexpr size_t MAX_COINS_CACHE_BYTES = 262144 + 512;
40-
41-
// Without any coins in the cache, we shouldn't need to flush.
42-
BOOST_TEST(
43-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0) != CoinsCacheSizeState::CRITICAL);
44-
45-
// If the initial memory allocations of cacheCoins don't match these common
46-
// cases, we can't really continue to make assertions about memory usage.
47-
// End the test early.
48-
if (view.DynamicMemoryUsage() != 32 && view.DynamicMemoryUsage() != 16) {
49-
// Add a bunch of coins to see that we at least flip over to CRITICAL.
50-
51-
for (int i{0}; i < 1000; ++i) {
52-
const COutPoint res = AddTestCoin(m_rng, view);
53-
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
23+
CCoinsViewCache& view{chainstate.CoinsTip()};
24+
25+
// Sanity: an empty cache should be ≲ 1 chunk (~ 256 KiB).
26+
BOOST_CHECK_LT(view.DynamicMemoryUsage() / (256 * 1024.0), 1.1);
27+
28+
constexpr size_t MAX_COINS_BYTES{8_MiB};
29+
constexpr size_t MAX_MEMPOOL_BYTES{4_MiB};
30+
constexpr size_t MAX_ATTEMPTS{50'000};
31+
32+
// Run the same growth-path twice: first with 0 head-room, then with extra head-room
33+
for (size_t max_mempool_size_bytes : {size_t{0}, MAX_MEMPOOL_BYTES}) {
34+
const int64_t full_cap{int64_t(MAX_COINS_BYTES + max_mempool_size_bytes)};
35+
const int64_t large_cap{LargeCoinsCacheThreshold(full_cap)};
36+
37+
// OK → LARGE
38+
auto state{chainstate.GetCoinsCacheSizeState(MAX_COINS_BYTES, max_mempool_size_bytes)};
39+
for (size_t i{0}; i < MAX_ATTEMPTS && int64_t(view.DynamicMemoryUsage()) <= large_cap; ++i) {
40+
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::OK);
41+
AddTestCoin(m_rng, view);
42+
state = chainstate.GetCoinsCacheSizeState(MAX_COINS_BYTES, max_mempool_size_bytes);
5443
}
5544

56-
BOOST_CHECK_EQUAL(
57-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
58-
CoinsCacheSizeState::CRITICAL);
59-
60-
BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
61-
return;
62-
}
63-
64-
print_view_mem_usage(view);
65-
BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32U : 16U);
66-
67-
// We should be able to add COINS_UNTIL_CRITICAL coins to the cache before going CRITICAL.
68-
// This is contingent not only on the dynamic memory usage of the Coins
69-
// that we're adding (COIN_SIZE bytes per), but also on how much memory the
70-
// cacheCoins (unordered_map) preallocates.
71-
constexpr int COINS_UNTIL_CRITICAL{3};
72-
73-
// no coin added, so we have plenty of space left.
74-
BOOST_CHECK_EQUAL(
75-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
76-
CoinsCacheSizeState::OK);
77-
78-
for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
79-
const COutPoint res = AddTestCoin(m_rng, view);
80-
print_view_mem_usage(view);
81-
BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
82-
83-
// adding first coin causes the MemoryResource to allocate one 256 KiB chunk of memory,
84-
// pushing us immediately over to LARGE
85-
BOOST_CHECK_EQUAL(
86-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0),
87-
CoinsCacheSizeState::LARGE);
88-
}
89-
90-
// Adding some additional coins will push us over the edge to CRITICAL.
91-
for (int i{0}; i < 4; ++i) {
92-
AddTestCoin(m_rng, view);
93-
print_view_mem_usage(view);
94-
if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0) ==
95-
CoinsCacheSizeState::CRITICAL) {
96-
break;
45+
// LARGE → CRITICAL
46+
for (size_t i{0}; i < MAX_ATTEMPTS && int64_t(view.DynamicMemoryUsage()) <= full_cap; ++i) {
47+
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::LARGE);
48+
AddTestCoin(m_rng, view);
49+
state = chainstate.GetCoinsCacheSizeState(MAX_COINS_BYTES, max_mempool_size_bytes);
9750
}
51+
BOOST_CHECK_EQUAL(state, CoinsCacheSizeState::CRITICAL);
9852
}
9953

100-
BOOST_CHECK_EQUAL(
101-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
102-
CoinsCacheSizeState::CRITICAL);
103-
104-
// Passing non-zero max mempool usage (512 KiB) should allow us more headroom.
105-
BOOST_CHECK_EQUAL(
106-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
107-
CoinsCacheSizeState::OK);
108-
109-
for (int i{0}; i < 3; ++i) {
110-
AddTestCoin(m_rng, view);
111-
print_view_mem_usage(view);
112-
BOOST_CHECK_EQUAL(
113-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
114-
CoinsCacheSizeState::OK);
115-
}
116-
117-
// Adding another coin with the additional mempool room will put us >90%
118-
// but not yet critical.
119-
AddTestCoin(m_rng, view);
120-
print_view_mem_usage(view);
121-
122-
// Only perform these checks on 64 bit hosts; I haven't done the math for 32.
123-
if (is_64_bit) {
124-
float usage_percentage = (float)view.DynamicMemoryUsage() / (MAX_COINS_CACHE_BYTES + (1 << 10));
125-
BOOST_TEST_MESSAGE("CoinsTip usage percentage: " << usage_percentage);
126-
BOOST_CHECK(usage_percentage >= 0.9);
127-
BOOST_CHECK(usage_percentage < 1);
128-
BOOST_CHECK_EQUAL(
129-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10), // 1024
130-
CoinsCacheSizeState::LARGE);
131-
}
132-
133-
// Using the default max_* values permits way more coins to be added.
134-
for (int i{0}; i < 1000; ++i) {
54+
// Default thresholds (no explicit limits) permit many more coins.
55+
for (int i{0}; i < 1'000; ++i) {
13556
AddTestCoin(m_rng, view);
136-
BOOST_CHECK_EQUAL(
137-
chainstate.GetCoinsCacheSizeState(),
138-
CoinsCacheSizeState::OK);
57+
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(), CoinsCacheSizeState::OK);
13958
}
14059

141-
// Flushing the view does take us back to OK because ReallocateCache() is called
142-
143-
BOOST_CHECK_EQUAL(
144-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
145-
CoinsCacheSizeState::CRITICAL);
146-
60+
// CRITICAL → OK via Flush
61+
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(MAX_COINS_BYTES, /*max_mempool_size_bytes=*/0), CoinsCacheSizeState::CRITICAL);
14762
view.SetBestBlock(m_rng.rand256());
148-
BOOST_CHECK(view.Flush());
149-
print_view_mem_usage(view);
150-
151-
BOOST_CHECK_EQUAL(
152-
chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
153-
CoinsCacheSizeState::OK);
63+
BOOST_REQUIRE(view.Flush());
64+
BOOST_CHECK_EQUAL(chainstate.GetCoinsCacheSizeState(MAX_COINS_BYTES, /*max_mempool_size_bytes=*/0), CoinsCacheSizeState::OK);
15465
}
15566

15667
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)