Skip to content

Commit

Permalink
kernel: Move block tree db open to block manager
Browse files Browse the repository at this point in the history
Before this change the block tree db was needlessly re-opened during
startup when loading a completed snapshot. Improve this by letting the
block manager open it on construction. This also simplifies the test
code a bit.

The change was initially motivated to make it easier for users of the
kernel library to instantiate a BlockManager that may be used to read
data from disk without loading the block index into a cache.
  • Loading branch information
TheCharlatan authored and josibake committed Sep 27, 2024
1 parent e9d60af commit 85895b8
Show file tree
Hide file tree
Showing 14 changed files with 60 additions and 48 deletions.
10 changes: 6 additions & 4 deletions src/bitcoin-chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ int main(int argc, char* argv[])
};
auto notifications = std::make_unique<KernelNotifications>();

node::CacheSizes cache_sizes;
cache_sizes.block_tree_db = 2 << 20;
cache_sizes.coins_db = 2 << 22;
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);

// SETUP: Chainstate
auto chainparams = CChainParams::Main();
Expand All @@ -119,14 +123,12 @@ int main(int argc, char* argv[])
.chainparams = chainman_opts.chainparams,
.blocks_dir = abs_datadir / "blocks",
.notifications = chainman_opts.notifications,
.block_tree_db_dir = abs_datadir / "blocks" / "index",
.block_tree_db_cache_size = 2 << 20,
};
util::SignalInterrupt interrupt;
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};

node::CacheSizes cache_sizes;
cache_sizes.block_tree_db = 2 << 20;
cache_sizes.coins_db = 2 << 22;
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
node::ChainstateLoadOptions options;
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
if (status != node::ChainstateLoadStatus::SUCCESS) {
Expand Down
9 changes: 8 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "dbwrapper.h"
#include <config/bitcoin-config.h> // IWYU pragma: keep

#include <init.h>
Expand Down Expand Up @@ -1069,6 +1070,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
.chainparams = chainman_opts_dummy.chainparams,
.blocks_dir = args.GetBlocksDirPath(),
.notifications = chainman_opts_dummy.notifications,
.block_tree_db_dir = args.GetDataDirNet() / "blocks" / "index",
.block_tree_db_cache_size = 0,
};
auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)};
if (!blockman_result) {
Expand Down Expand Up @@ -1217,10 +1220,15 @@ static ChainstateLoadResult InitAndLoadChainstate(
.chainparams = chainman_opts.chainparams,
.blocks_dir = args.GetBlocksDirPath(),
.notifications = chainman_opts.notifications,
.block_tree_db_dir = args.GetDataDirNet() / "blocks" / "index",
.wipe_block_tree_db = do_reindex,
.block_tree_db_cache_size = cache_sizes.block_tree_db,
};
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
try {
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
} catch (dbwrapper_error&) {
return {ChainstateLoadStatus::FAILURE, strprintf(Untranslated("Error opening block database."))};
} catch (std::exception& e) {
return {ChainstateLoadStatus::FAILURE_FATAL, strprintf(Untranslated("Failed to initialize ChainstateManager: %s"), e.what())};
}
Expand All @@ -1247,7 +1255,6 @@ static ChainstateLoadResult InitAndLoadChainstate(
};
node::ChainstateLoadOptions options;
options.mempool = Assert(node.mempool.get());
options.wipe_block_tree_db = do_reindex;
options.wipe_chainstate_db = do_reindex || do_reindex_chainstate;
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
Expand Down
9 changes: 9 additions & 0 deletions src/kernel/blockmanager_opts.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H
#define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H

#include <dbwrapper.h>
#include <kernel/notifications_interface.h>
#include <util/fs.h>

Expand All @@ -27,6 +28,14 @@ struct BlockManagerOpts {
bool fast_prune{false};
const fs::path blocks_dir;
Notifications& notifications;
const fs::path block_tree_db_dir;
// Whether to wipe the block tree database when loading it. If set, this
// will also set a reindexing flag so any existing block data files will be
// scanned and added to the database.
bool wipe_block_tree_db{false};
bool block_tree_db_in_memory{false};
DBOptions block_tree_db_options{};
int64_t block_tree_db_cache_size;
};

} // namespace kernel
Expand Down
1 change: 0 additions & 1 deletion src/kernel/chainstatemanager_opts.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ struct ChainstateManagerOpts {
std::optional<uint256> assumed_valid_block{};
//! If the tip is older than this, the node is considered to be in initial block download.
std::chrono::seconds max_tip_age{DEFAULT_MAX_TIP_AGE};
DBOptions block_tree_db{};
DBOptions coins_db{};
CoinsViewOptions coins_view{};
Notifications& notifications;
Expand Down
3 changes: 3 additions & 0 deletions src/node/blockmanager_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <common/args.h>
#include <node/blockstorage.h>
#include <node/database_args.h>
#include <tinyformat.h>
#include <util/result.h>
#include <util/translation.h>
Expand Down Expand Up @@ -34,6 +35,8 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Op

if (auto value{args.GetBoolArg("-fastprune")}) opts.fast_prune = *value;

ReadDatabaseArgs(args, opts.block_tree_db_options);

return {};
}
} // namespace node
24 changes: 21 additions & 3 deletions src/node/blockstorage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <util/translation.h>
#include <validation.h>

#include <cstddef>
#include <map>
#include <ranges>
#include <unordered_map>
Expand Down Expand Up @@ -1180,10 +1181,27 @@ static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
BlockManager::BlockManager(const util::SignalInterrupt& interrupt, Options opts)
: m_prune_mode{opts.prune_target > 0},
m_xor_key{InitBlocksdirXorKey(opts)},
m_block_file_seq{FlatFileSeq{opts.blocks_dir, "blk", opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},
m_undo_file_seq{FlatFileSeq{opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},
m_opts{std::move(opts)},
m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},
m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},
m_interrupt{interrupt} {}
m_interrupt{interrupt}
{
m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
.path = m_opts.block_tree_db_dir,
.cache_bytes = static_cast<size_t>(m_opts.block_tree_db_cache_size),
.memory_only = m_opts.block_tree_db_in_memory,
.wipe_data = m_opts.wipe_block_tree_db,
.options = m_opts.block_tree_db_options});

if (m_opts.wipe_block_tree_db) {
m_block_tree_db->WriteReindexing(true);
m_blockfiles_indexed = false;
// If we're reindexing in prune mode, wipe away unusable block files and all undo data files
if (m_prune_mode) {
CleanupBlockRevFiles();
}
}
}

class ImportingNow
{
Expand Down
3 changes: 1 addition & 2 deletions src/node/blockstorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,6 @@ class BlockManager

BlockfileType BlockfileTypeForHeight(int height);

const kernel::BlockManagerOpts m_opts;

const FlatFileSeq m_block_file_seq;
const FlatFileSeq m_undo_file_seq;

Expand All @@ -269,6 +267,7 @@ class BlockManager

explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);

const kernel::BlockManagerOpts m_opts;
const util::SignalInterrupt& m_interrupt;
std::atomic<bool> m_importing{false};

Expand Down
22 changes: 1 addition & 21 deletions src/node/chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,6 @@ static ChainstateLoadResult CompleteChainstateInitialization(
const CacheSizes& cache_sizes,
const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
auto& pblocktree{chainman.m_blockman.m_block_tree_db};
// new BlockTreeDB tries to delete the existing file, which
// fails if it's still open from the previous loop. Close it first:
pblocktree.reset();
pblocktree = std::make_unique<BlockTreeDB>(DBParams{
.path = chainman.m_options.datadir / "blocks" / "index",
.cache_bytes = static_cast<size_t>(cache_sizes.block_tree_db),
.memory_only = options.block_tree_db_in_memory,
.wipe_data = options.wipe_block_tree_db,
.options = chainman.m_options.block_tree_db});

if (options.wipe_block_tree_db) {
pblocktree->WriteReindexing(true);
chainman.m_blockman.m_blockfiles_indexed = false;
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
if (options.prune) {
chainman.m_blockman.CleanupBlockRevFiles();
}
}

if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};

// LoadBlockIndex will load m_have_pruned if we've ever removed a
Expand Down Expand Up @@ -142,7 +122,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(
}
}

if (!options.wipe_block_tree_db) {
if (!chainman.m_blockman.m_opts.wipe_block_tree_db) {
auto chainstates{chainman.GetAll()};
if (std::any_of(chainstates.begin(), chainstates.end(),
[](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
Expand Down
5 changes: 0 additions & 5 deletions src/node/chainstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ struct CacheSizes;

struct ChainstateLoadOptions {
CTxMemPool* mempool{nullptr};
bool block_tree_db_in_memory{false};
bool coins_db_in_memory{false};
// Whether to wipe the block tree database when loading it. If set, this
// will also set a reindexing flag so any existing block data files will be
// scanned and added to the database.
bool wipe_block_tree_db{false};
// Whether to wipe the chainstate database when loading it. If set, this
// will cause the chainstate database to be rebuilt starting from genesis.
bool wipe_chainstate_db{false};
Expand Down
1 change: 0 additions & 1 deletion src/node/chainstatemanager_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage

if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value};

ReadDatabaseArgs(args, opts.block_tree_db);
ReadDatabaseArgs(args, opts.coins_db);
ReadCoinsViewArgs(args, opts.coins_view);

Expand Down
4 changes: 4 additions & 0 deletions src/test/blockmanager_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos)
.chainparams = *params,
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = notifications,
.block_tree_db_dir = m_args.GetDataDirNet() / "blocks" / "index",
.block_tree_db_cache_size = 0,
};
BlockManager blockman{*Assert(m_node.shutdown), blockman_opts};
// simulate adding a genesis block normally
Expand Down Expand Up @@ -140,6 +142,8 @@ BOOST_AUTO_TEST_CASE(blockmanager_flush_block_file)
.chainparams = Params(),
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = notifications,
.block_tree_db_dir = m_args.GetDataDirNet() / "blocks" / "index",
.block_tree_db_cache_size = 0,
};
BlockManager blockman{*Assert(m_node.shutdown), blockman_opts};

Expand Down
13 changes: 4 additions & 9 deletions src/test/util/setup_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
#include <stdexcept>

using namespace util::hex_literals;
using kernel::BlockTreeDB;
using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::BlockManager;
Expand Down Expand Up @@ -244,14 +243,13 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
.chainparams = chainman_opts.chainparams,
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = chainman_opts.notifications,
.block_tree_db_dir = m_args.GetDataDirNet() / "blocks" / "index",
.wipe_block_tree_db = m_args.GetBoolArg("-reindex", false),
.block_tree_db_in_memory = opts.block_tree_db_in_memory,
.block_tree_db_cache_size = m_cache_sizes.block_tree_db,
};
m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown), chainman_opts, blockman_opts);
LOCK(m_node.chainman->GetMutex());
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db),
.memory_only = true,
});
};
m_make_chainman();
}
Expand All @@ -277,9 +275,7 @@ void ChainTestingSetup::LoadVerifyActivateChainstate()
auto& chainman{*Assert(m_node.chainman)};
node::ChainstateLoadOptions options;
options.mempool = Assert(m_node.mempool.get());
options.block_tree_db_in_memory = m_block_tree_db_in_memory;
options.coins_db_in_memory = m_coins_db_in_memory;
options.wipe_block_tree_db = m_args.GetBoolArg("-reindex", false);
options.wipe_chainstate_db = m_args.GetBoolArg("-reindex", false) || m_args.GetBoolArg("-reindex-chainstate", false);
options.prune = chainman.m_blockman.IsPruneMode();
options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
Expand All @@ -303,7 +299,6 @@ TestingSetup::TestingSetup(
: ChainTestingSetup(chainType, opts)
{
m_coins_db_in_memory = opts.coins_db_in_memory;
m_block_tree_db_in_memory = opts.block_tree_db_in_memory;
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RegisterAllCoreRPCCommands(tableRPC);
Expand Down
1 change: 0 additions & 1 deletion src/test/util/setup_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ struct BasicTestingSetup {
struct ChainTestingSetup : public BasicTestingSetup {
node::CacheSizes m_cache_sizes{};
bool m_coins_db_in_memory{true};
bool m_block_tree_db_in_memory{true};
std::function<void()> m_make_chainman{};

explicit ChainTestingSetup(const ChainType chainType = ChainType::MAIN, TestOpts = {});
Expand Down
3 changes: 3 additions & 0 deletions src/test/validation_chainstatemanager_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ struct SnapshotTestSetup : TestChain100Setup {
.chainparams = chainman_opts.chainparams,
.blocks_dir = m_args.GetBlocksDirPath(),
.notifications = chainman_opts.notifications,
.block_tree_db_dir = chainman.m_blockman.m_opts.block_tree_db_dir,
.block_tree_db_in_memory = chainman.m_blockman.m_opts.block_tree_db_in_memory,
.block_tree_db_cache_size = chainman.m_blockman.m_opts.block_tree_db_cache_size,
};
// For robustness, ensure the old manager is destroyed before creating a
// new one.
Expand Down

0 comments on commit 85895b8

Please sign in to comment.