index: remove CBlockIndex access from CustomAppend()

Moved CBlockUndo disk read lookups from child index classes to
the base index class.

The goal is for child index classes to synchronize only through
events, without directly accessing the chain database.

This change will enable future parallel synchronization mechanisms,
reduce database access (when batched), and contribute toward the
goal of running indexes in a separate process (with no chain
database access).

Besides that, this commit also documents how NextSyncBlock() behaves.
It is not immediately clear this function could return the first
block after the fork point during a reorg.
This commit is contained in:
furszy
2023-02-18 10:39:24 -03:00
parent 91b7ab6c69
commit 029ba1a21d
6 changed files with 55 additions and 43 deletions

View File

@@ -144,9 +144,44 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
return pindex; return pindex;
} }
// Since block is not in the chain, return the next block in the chain AFTER the last common ancestor.
// Caller will be responsible for rewinding back to the common ancestor.
return chain.Next(chain.FindFork(pindex_prev)); return chain.Next(chain.FindFork(pindex_prev));
} }
bool BaseIndex::ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data)
{
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block_data);
CBlock block;
if (!block_data) { // disk lookup if block data wasn't provided
if (!m_chainstate->m_blockman.ReadBlock(block, *pindex)) {
FatalErrorf("%s: Failed to read block %s from disk",
__func__, pindex->GetBlockHash().ToString());
return false;
}
block_info.data = █
}
CBlockUndo block_undo;
if (CustomOptions().connect_undo_data) {
if (pindex->nHeight > 0 && !m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
FatalErrorf("%s: Failed to read undo block data %s from disk",
__func__, pindex->GetBlockHash().ToString());
return false;
}
block_info.undo_data = &block_undo;
}
if (!CustomAppend(block_info)) {
FatalErrorf("%s: Failed to write block %s to index database",
__func__, pindex->GetBlockHash().ToString());
return false;
}
return true;
}
void BaseIndex::Sync() void BaseIndex::Sync()
{ {
const CBlockIndex* pindex = m_best_block_index.load(); const CBlockIndex* pindex = m_best_block_index.load();
@@ -192,20 +227,7 @@ void BaseIndex::Sync()
pindex = pindex_next; pindex = pindex_next;
CBlock block; if (!ProcessBlock(pindex)) return; // error logged internally
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex);
if (!m_chainstate->m_blockman.ReadBlock(block, *pindex)) {
FatalErrorf("%s: Failed to read block %s from disk",
__func__, pindex->GetBlockHash().ToString());
return;
} else {
block_info.data = █
}
if (!CustomAppend(block_info)) {
FatalErrorf("%s: Failed to write block %s to index database",
__func__, pindex->GetBlockHash().ToString());
return;
}
auto current_time{std::chrono::steady_clock::now()}; auto current_time{std::chrono::steady_clock::now()};
if (last_log_time + SYNC_LOG_INTERVAL < current_time) { if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
@@ -337,17 +359,14 @@ void BaseIndex::BlockConnected(ChainstateRole role, const std::shared_ptr<const
return; return;
} }
} }
interfaces::BlockInfo block_info = kernel::MakeBlockInfo(pindex, block.get());
if (CustomAppend(block_info)) { // Dispatch block to child class; errors are logged internally and abort the node.
if (ProcessBlock(pindex, block.get())) {
// Setting the best block index is intentionally the last step of this // Setting the best block index is intentionally the last step of this
// function, so BlockUntilSyncedToCurrentChain callers waiting for the // function, so BlockUntilSyncedToCurrentChain callers waiting for the
// best block index to be updated can rely on the block being fully // best block index to be updated can rely on the block being fully
// processed, and the index object being safe to delete. // processed, and the index object being safe to delete.
SetBestBlockIndex(pindex); SetBestBlockIndex(pindex);
} else {
FatalErrorf("%s: Failed to write block %s to index",
__func__, pindex->GetBlockHash().ToString());
return;
} }
} }

View File

@@ -93,6 +93,8 @@ private:
/// Loop over disconnected blocks and call CustomRemove. /// Loop over disconnected blocks and call CustomRemove.
bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip); bool Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip);
bool ProcessBlock(const CBlockIndex* pindex, const CBlock* block_data = nullptr);
virtual bool AllowPrune() const = 0; virtual bool AllowPrune() const = 0;
template <typename... Args> template <typename... Args>

View File

@@ -13,7 +13,6 @@
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <undo.h> #include <undo.h>
#include <util/fs_helpers.h> #include <util/fs_helpers.h>
#include <validation.h>
/* The index database stores three items for each block: the disk location of the encoded filter, /* The index database stores three items for each block: the disk location of the encoded filter,
* its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by * its dSHA256 hash, and the header. Those belonging to blocks on the active chain are indexed by
@@ -112,6 +111,13 @@ BlockFilterIndex::BlockFilterIndex(std::unique_ptr<interfaces::Chain> chain, Blo
m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE); m_filter_fileseq = std::make_unique<FlatFileSeq>(std::move(path), "fltr", FLTR_FILE_CHUNK_SIZE);
} }
interfaces::Chain::NotifyOptions BlockFilterIndex::CustomOptions()
{
interfaces::Chain::NotifyOptions options;
options.connect_undo_data = true;
return options;
}
bool BlockFilterIndex::CustomInit(const std::optional<interfaces::BlockRef>& block) bool BlockFilterIndex::CustomInit(const std::optional<interfaces::BlockRef>& block)
{ {
if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) { if (!m_db->Read(DB_FILTER_POS, m_next_filter_pos)) {
@@ -250,19 +256,7 @@ std::optional<uint256> BlockFilterIndex::ReadFilterHeader(int height, const uint
bool BlockFilterIndex::CustomAppend(const interfaces::BlockInfo& block) bool BlockFilterIndex::CustomAppend(const interfaces::BlockInfo& block)
{ {
CBlockUndo block_undo; BlockFilter filter(m_filter_type, *Assert(block.data), *Assert(block.undo_data));
if (block.height > 0) {
// pindex variable gives indexing code access to node internals. It
// will be removed in upcoming commit
const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash));
if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
return false;
}
}
BlockFilter filter(m_filter_type, *Assert(block.data), block_undo);
const uint256& header = filter.ComputeHeader(m_last_header); const uint256& header = filter.ComputeHeader(m_last_header);
bool res = Write(filter, block.height, header); bool res = Write(filter, block.height, header);
if (res) m_last_header = header; // update last header if (res) m_last_header = header; // update last header

View File

@@ -52,6 +52,8 @@ private:
std::optional<uint256> ReadFilterHeader(int height, const uint256& expected_block_hash); std::optional<uint256> ReadFilterHeader(int height, const uint256& expected_block_hash);
protected: protected:
interfaces::Chain::NotifyOptions CustomOptions() override;
bool CustomInit(const std::optional<interfaces::BlockRef>& block) override; bool CustomInit(const std::optional<interfaces::BlockRef>& block) override;
bool CustomCommit(CDBBatch& batch) override; bool CustomCommit(CDBBatch& batch) override;

View File

@@ -114,19 +114,11 @@ CoinStatsIndex::CoinStatsIndex(std::unique_ptr<interfaces::Chain> chain, size_t
bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block) bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
{ {
CBlockUndo block_undo;
const CAmount block_subsidy{GetBlockSubsidy(block.height, Params().GetConsensus())}; const CAmount block_subsidy{GetBlockSubsidy(block.height, Params().GetConsensus())};
m_total_subsidy += block_subsidy; m_total_subsidy += block_subsidy;
// Ignore genesis block // Ignore genesis block
if (block.height > 0) { if (block.height > 0) {
// pindex variable gives indexing code access to node internals. It
// will be removed in upcoming commit
const CBlockIndex* pindex = WITH_LOCK(cs_main, return m_chainstate->m_blockman.LookupBlockIndex(block.hash));
if (!m_chainstate->m_blockman.ReadBlockUndo(block_undo, *pindex)) {
return false;
}
std::pair<uint256, DBVal> read_out; std::pair<uint256, DBVal> read_out;
if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) { if (!m_db->Read(DBHeightKey(block.height - 1), read_out)) {
return false; return false;
@@ -183,7 +175,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
// The coinbase tx has no undo data since no former output is spent // The coinbase tx has no undo data since no former output is spent
if (!tx->IsCoinBase()) { if (!tx->IsCoinBase()) {
const auto& tx_undo{block_undo.vtxundo.at(i - 1)}; const auto& tx_undo{Assert(block.undo_data)->vtxundo.at(i - 1)};
for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) { for (size_t j = 0; j < tx_undo.vprevout.size(); ++j) {
Coin coin{tx_undo.vprevout[j]}; Coin coin{tx_undo.vprevout[j]};
@@ -383,6 +375,7 @@ bool CoinStatsIndex::CustomCommit(CDBBatch& batch)
interfaces::Chain::NotifyOptions CoinStatsIndex::CustomOptions() interfaces::Chain::NotifyOptions CoinStatsIndex::CustomOptions()
{ {
interfaces::Chain::NotifyOptions options; interfaces::Chain::NotifyOptions options;
options.connect_undo_data = true;
options.disconnect_data = true; options.disconnect_data = true;
options.disconnect_undo_data = true; options.disconnect_undo_data = true;
return options; return options;

View File

@@ -330,6 +330,8 @@ public:
//! Options specifying which chain notifications are required. //! Options specifying which chain notifications are required.
struct NotifyOptions struct NotifyOptions
{ {
//! Include undo data with block connected notifications.
bool connect_undo_data = false;
//! Include block data with block disconnected notifications. //! Include block data with block disconnected notifications.
bool disconnect_data = false; bool disconnect_data = false;
//! Include undo data with block disconnected notifications. //! Include undo data with block disconnected notifications.