Skip to content

Commit df8bf65

Browse files
committed
Merge bitcoin/bitcoin#31483: kernel: Move kernel-related cache constants to kernel cache
2a92702 init: Use size_t consistently for cache sizes (TheCharlatan) 65cde36 kernel: Move default cache constants to caches (TheCharlatan) 8826cae kernel: Move non-kernel db cache size constants (TheCharlatan) e758b26 kernel: Move kernel-specific cache size options to kernel (TheCharlatan) d5e2c4a fuzz: Add fuzz test for checked and saturating add and left shift (TheCharlatan) c03a279 util: Add integer left shift helpers (TheCharlatan) 8bd5f8a [refactor] init: Simplify coinsdb cache calculation (TheCharlatan) 5db7d4d doc: Correct docstring describing max block tree db cache (TheCharlatan) Pull request description: Carrying non-kernel related fields in the cache sizes for the indexes is confusing for kernel library users. The cache sizes are set currently with magic numbers in bitcoin-chainstate. The comments for the cache size calculations are not completely clear. The constants for the cache sizes are also currently in `txdb.h`, which is not an ideal place for holding all cache size related constants. Solve these things by moving the kernel-specific cache size fields to their own struct and moving the constants to either the node or the kernel cache sizes. This slightly changes the way the cache is allocated if (and only if) the txindex and/or blockfilterindex is used. Since they are now given precedence over the block tree db cache, this results in a bit less cache being allocated to the block tree db, coinsdb and coins caches. The effect is negligible though, i.e. cache sizes with default dbcache reported through the logs are: master: ``` Cache configuration: * Using 2.0 MiB for block index database * Using 56.0 MiB for transaction index database * Using 49.0 MiB for basic block filter index database * Using 8.0 MiB for chain state database * Using 335.0 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space) ``` this PR: ``` Cache configuration: * Using 2.0 MiB for block index database * Using 56.2 MiB for transaction index database * Using 49.2 MiB for basic block filter index database * Using 8.0 MiB for chain state database * Using 334.5 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space) ``` --- This PR is part of the [libbitcoinkernel project](bitcoin/bitcoin#27587). ACKs for top commit: stickies-v: re-ACK 2a92702 ryanofsky: Code review ACK 2a92702. Changes since last review are fixing size options to use size_t instead of int64_t again, simplifying CheckedLeftShift more, and making other minor suggested cleanups hodlinator: re-ACK 2a92702 Tree-SHA512: 98376eaa0660b1b8c096a5ce1f3e7c8c30e7cd6644de36856c2d3e573108cfc9473c93ebb3952b7881047b5ae6c85c5b096e6726f30f35be58b98eca07c8c785
2 parents 335798c + 2a92702 commit df8bf65

18 files changed

+321
-75
lines changed

src/bitcoin-chainstate.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919

2020
#include <consensus/validation.h>
2121
#include <core_io.h>
22+
#include <kernel/caches.h>
2223
#include <logging.h>
2324
#include <node/blockstorage.h>
24-
#include <node/caches.h>
2525
#include <node/chainstate.h>
2626
#include <random.h>
2727
#include <script/sigcache.h>
@@ -123,10 +123,7 @@ int main(int argc, char* argv[])
123123
util::SignalInterrupt interrupt;
124124
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
125125

126-
node::CacheSizes cache_sizes;
127-
cache_sizes.block_tree_db = 2 << 20;
128-
cache_sizes.coins_db = 2 << 22;
129-
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
126+
kernel::CacheSizes cache_sizes{DEFAULT_KERNEL_CACHE};
130127
node::ChainstateLoadOptions options;
131128
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
132129
if (status != node::ChainstateLoadStatus::SUCCESS) {

src/init.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <interfaces/ipc.h>
3333
#include <interfaces/mining.h>
3434
#include <interfaces/node.h>
35+
#include <kernel/caches.h>
3536
#include <kernel/context.h>
3637
#include <key.h>
3738
#include <logging.h>
@@ -121,7 +122,6 @@ using common::ResolveErrMsg;
121122

122123
using node::ApplyArgsManOptions;
123124
using node::BlockManager;
124-
using node::CacheSizes;
125125
using node::CalculateCacheSizes;
126126
using node::ChainstateLoadResult;
127127
using node::ChainstateLoadStatus;
@@ -487,7 +487,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
487487
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
488488
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
489489
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
490-
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (minimum %d, default: %d). Make sure you have enough RAM. In addition, unused memory allocated to the mempool is shared with this cache (see -maxmempool).", nMinDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
490+
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (minimum %d, default: %d). Make sure you have enough RAM. In addition, unused memory allocated to the mempool is shared with this cache (see -maxmempool).", MIN_DB_CACHE >> 20, DEFAULT_DB_CACHE >> 20), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
491491
argsman.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
492492
argsman.AddArg("-allowignoredconf", strprintf("For backwards compatibility, treat an unused %s file in the datadir as a warning, not an error.", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
493493
argsman.AddArg("-loadblock=<file>", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -1177,7 +1177,7 @@ static ChainstateLoadResult InitAndLoadChainstate(
11771177
NodeContext& node,
11781178
bool do_reindex,
11791179
const bool do_reindex_chainstate,
1180-
CacheSizes& cache_sizes,
1180+
const kernel::CacheSizes& cache_sizes,
11811181
const ArgsManager& args)
11821182
{
11831183
const CChainParams& chainparams = Params();
@@ -1602,18 +1602,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16021602
ReadNotificationArgs(args, kernel_notifications);
16031603

16041604
// cache size calculations
1605-
CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size());
1605+
const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
16061606

1607-
LogPrintf("Cache configuration:\n");
1608-
LogPrintf("* Using %.1f MiB for block index database\n", cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
1607+
LogInfo("Cache configuration:");
1608+
LogInfo("* Using %.1f MiB for block index database", kernel_cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
16091609
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
1610-
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
1610+
LogInfo("* Using %.1f MiB for transaction index database", index_cache_sizes.tx_index * (1.0 / 1024 / 1024));
16111611
}
16121612
for (BlockFilterType filter_type : g_enabled_filter_types) {
1613-
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
1614-
cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
1613+
LogInfo("* Using %.1f MiB for %s block filter index database",
1614+
index_cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
16151615
}
1616-
LogPrintf("* Using %.1f MiB for chain state database\n", cache_sizes.coins_db * (1.0 / 1024 / 1024));
1616+
LogInfo("* Using %.1f MiB for chain state database", kernel_cache_sizes.coins_db * (1.0 / 1024 / 1024));
16171617

16181618
assert(!node.mempool);
16191619
assert(!node.chainman);
@@ -1626,7 +1626,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16261626
node,
16271627
do_reindex,
16281628
do_reindex_chainstate,
1629-
cache_sizes,
1629+
kernel_cache_sizes,
16301630
args);
16311631
if (status == ChainstateLoadStatus::FAILURE && !do_reindex && !ShutdownRequested(node)) {
16321632
// suggest a reindex
@@ -1645,7 +1645,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16451645
node,
16461646
do_reindex,
16471647
do_reindex_chainstate,
1648-
cache_sizes,
1648+
kernel_cache_sizes,
16491649
args);
16501650
}
16511651
if (status != ChainstateLoadStatus::SUCCESS && status != ChainstateLoadStatus::INTERRUPTED) {
@@ -1671,12 +1671,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
16711671
// ********************************************************* Step 8: start indexers
16721672

16731673
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
1674-
g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), cache_sizes.tx_index, false, do_reindex);
1674+
g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), index_cache_sizes.tx_index, false, do_reindex);
16751675
node.indexes.emplace_back(g_txindex.get());
16761676
}
16771677

16781678
for (const auto& filter_type : g_enabled_filter_types) {
1679-
InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, cache_sizes.filter_index, false, do_reindex);
1679+
InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, index_cache_sizes.filter_index, false, do_reindex);
16801680
node.indexes.emplace_back(GetBlockFilterIndex(filter_type));
16811681
}
16821682

src/kernel/caches.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_KERNEL_CACHES_H
6+
#define BITCOIN_KERNEL_CACHES_H
7+
8+
#include <util/byte_units.h>
9+
10+
#include <algorithm>
11+
12+
//! Suggested default amount of cache reserved for the kernel (bytes)
13+
static constexpr size_t DEFAULT_KERNEL_CACHE{450_MiB};
14+
//! Max memory allocated to block tree DB specific cache (bytes)
15+
static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB};
16+
//! Max memory allocated to coin DB specific cache (bytes)
17+
static constexpr size_t MAX_COINS_DB_CACHE{8_MiB};
18+
19+
namespace kernel {
20+
struct CacheSizes {
21+
size_t block_tree_db;
22+
size_t coins_db;
23+
size_t coins;
24+
25+
CacheSizes(size_t total_cache)
26+
{
27+
block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
28+
total_cache -= block_tree_db;
29+
coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
30+
total_cache -= coins_db;
31+
coins = total_cache; // the rest goes to the coins cache
32+
}
33+
};
34+
} // namespace kernel
35+
36+
#endif // BITCOIN_KERNEL_CACHES_H

src/node/caches.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,39 @@
66

77
#include <common/args.h>
88
#include <index/txindex.h>
9-
#include <txdb.h>
9+
#include <kernel/caches.h>
10+
#include <logging.h>
11+
#include <util/byte_units.h>
12+
13+
#include <algorithm>
14+
#include <string>
15+
16+
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
17+
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
18+
//! Max memory allocated to tx index DB specific cache in bytes.
19+
static constexpr size_t MAX_TX_INDEX_CACHE{1024_MiB};
20+
//! Max memory allocated to all block filter index caches combined in bytes.
21+
static constexpr size_t MAX_FILTER_INDEX_CACHE{1024_MiB};
1022

1123
namespace node {
1224
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
1325
{
14-
int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
15-
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
16-
CacheSizes sizes;
17-
sizes.block_tree_db = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
18-
nTotalCache -= sizes.block_tree_db;
19-
sizes.tx_index = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
20-
nTotalCache -= sizes.tx_index;
21-
sizes.filter_index = 0;
26+
// Convert -dbcache from MiB units to bytes. The total cache is floored by MIN_DB_CACHE and capped by max size_t value.
27+
size_t total_cache{DEFAULT_DB_CACHE};
28+
if (std::optional<int64_t> db_cache = args.GetIntArg("-dbcache")) {
29+
if (*db_cache < 0) db_cache = 0;
30+
uint64_t db_cache_bytes = SaturatingLeftShift<uint64_t>(*db_cache, 20);
31+
total_cache = std::max<size_t>(MIN_DB_CACHE, std::min<uint64_t>(db_cache_bytes, std::numeric_limits<size_t>::max()));
32+
}
33+
34+
IndexCacheSizes index_sizes;
35+
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
36+
total_cache -= index_sizes.tx_index;
2237
if (n_indexes > 0) {
23-
int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
24-
sizes.filter_index = max_cache / n_indexes;
25-
nTotalCache -= sizes.filter_index * n_indexes;
38+
size_t max_cache = std::min(total_cache / 8, MAX_FILTER_INDEX_CACHE);
39+
index_sizes.filter_index = max_cache / n_indexes;
40+
total_cache -= index_sizes.filter_index * n_indexes;
2641
}
27-
sizes.coins_db = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
28-
sizes.coins_db = std::min(sizes.coins_db, nMaxCoinsDBCache << 20); // cap total coins db cache
29-
nTotalCache -= sizes.coins_db;
30-
sizes.coins = nTotalCache; // the rest goes to in-memory cache
31-
return sizes;
42+
return {index_sizes, kernel::CacheSizes{total_cache}};
3243
}
3344
} // namespace node

src/node/caches.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@
55
#ifndef BITCOIN_NODE_CACHES_H
66
#define BITCOIN_NODE_CACHES_H
77

8+
#include <kernel/caches.h>
9+
#include <util/byte_units.h>
10+
811
#include <cstddef>
9-
#include <cstdint>
1012

1113
class ArgsManager;
1214

15+
//! min. -dbcache (bytes)
16+
static constexpr size_t MIN_DB_CACHE{4_MiB};
17+
//! -dbcache default (bytes)
18+
static constexpr size_t DEFAULT_DB_CACHE{DEFAULT_KERNEL_CACHE};
19+
1320
namespace node {
21+
struct IndexCacheSizes {
22+
size_t tx_index{0};
23+
size_t filter_index{0};
24+
};
1425
struct CacheSizes {
15-
int64_t block_tree_db;
16-
int64_t coins_db;
17-
int64_t coins;
18-
int64_t tx_index;
19-
int64_t filter_index;
26+
IndexCacheSizes index;
27+
kernel::CacheSizes kernel;
2028
};
2129
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
2230
} // namespace node

src/node/chainstate.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
#include <chain.h>
99
#include <coins.h>
1010
#include <consensus/params.h>
11+
#include <kernel/caches.h>
1112
#include <logging.h>
1213
#include <node/blockstorage.h>
13-
#include <node/caches.h>
1414
#include <sync.h>
1515
#include <threadsafety.h>
1616
#include <tinyformat.h>
@@ -29,6 +29,8 @@
2929
#include <memory>
3030
#include <vector>
3131

32+
using kernel::CacheSizes;
33+
3234
namespace node {
3335
// Complete initialization of chainstates after the initial call has been made
3436
// to ChainstateManager::InitializeChainstate().
@@ -44,7 +46,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(
4446
try {
4547
pblocktree = std::make_unique<BlockTreeDB>(DBParams{
4648
.path = chainman.m_options.datadir / "blocks" / "index",
47-
.cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
49+
.cache_bytes = cache_sizes.block_tree_db,
4850
.memory_only = options.block_tree_db_in_memory,
4951
.wipe_data = options.wipe_block_tree_db,
5052
.options = chainman.m_options.block_tree_db});

src/node/chainstate.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
class CTxMemPool;
1616

17-
namespace node {
18-
17+
namespace kernel {
1918
struct CacheSizes;
19+
} // namespace kernel
20+
21+
namespace node {
2022

2123
struct ChainstateLoadOptions {
2224
CTxMemPool* mempool{nullptr};
@@ -69,7 +71,7 @@ using ChainstateLoadResult = std::tuple<ChainstateLoadStatus, bilingual_str>;
6971
*
7072
* LoadChainstate returns a (status code, error string) tuple.
7173
*/
72-
ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes,
74+
ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const kernel::CacheSizes& cache_sizes,
7375
const ChainstateLoadOptions& options);
7476
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options);
7577
} // namespace node

src/qt/optionsdialog.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
#include <common/system.h>
1717
#include <interfaces/node.h>
18-
#include <node/chainstatemanager_args.h>
1918
#include <netbase.h>
20-
#include <txdb.h>
19+
#include <node/caches.h>
20+
#include <node/chainstatemanager_args.h>
2121
#include <util/strencodings.h>
2222

2323
#include <chrono>
@@ -95,7 +95,7 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet)
9595
ui->verticalLayout->setStretchFactor(ui->tabWidget, 1);
9696

9797
/* Main elements init */
98-
ui->databaseCache->setRange(nMinDbCache, std::numeric_limits<int>::max());
98+
ui->databaseCache->setRange(MIN_DB_CACHE >> 20, std::numeric_limits<int>::max());
9999
ui->threadsScriptVerif->setMinimum(-GetNumCores());
100100
ui->threadsScriptVerif->setMaximum(MAX_SCRIPTCHECK_THREADS);
101101
ui->pruneWarning->setVisible(false);

src/qt/optionsmodel.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
#include <mapport.h>
1616
#include <net.h>
1717
#include <netbase.h>
18+
#include <node/caches.h>
1819
#include <node/chainstatemanager_args.h>
19-
#include <txdb.h> // for -dbcache defaults
2020
#include <util/string.h>
2121
#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
2222
#include <wallet/wallet.h> // For DEFAULT_SPEND_ZEROCONF_CHANGE
@@ -470,7 +470,7 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con
470470
suffix.empty() ? getOption(option, "-prev") :
471471
DEFAULT_PRUNE_TARGET_GB;
472472
case DatabaseCache:
473-
return qlonglong(SettingToInt(setting(), nDefaultDbCache));
473+
return qlonglong(SettingToInt(setting(), DEFAULT_DB_CACHE >> 20));
474474
case ThreadsScriptVerif:
475475
return qlonglong(SettingToInt(setting(), DEFAULT_SCRIPTCHECK_THREADS));
476476
case Listen:
@@ -733,7 +733,7 @@ void OptionsModel::checkAndMigrate()
733733
// see https://github.com/bitcoin/bitcoin/pull/8273
734734
// force people to upgrade to the new value if they are using 100MB
735735
if (settingsVersion < 130000 && settings.contains("nDatabaseCache") && settings.value("nDatabaseCache").toLongLong() == 100)
736-
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
736+
settings.setValue("nDatabaseCache", (qint64)(DEFAULT_DB_CACHE >> 20));
737737

738738
settings.setValue(strSettingsVersionKey, CLIENT_VERSION);
739739
}

src/test/fuzz/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_executable(fuzz
7171
netaddress.cpp
7272
netbase_dns_lookup.cpp
7373
node_eviction.cpp
74+
overflow.cpp
7475
p2p_handshake.cpp
7576
p2p_headers_presync.cpp
7677
p2p_transport_serialization.cpp

0 commit comments

Comments
 (0)