Skip to content

Commit ca91c24

Browse files
committed
index: verify blocks data existence only once
At present, during init, we traverse the chain (once per index) to confirm that all necessary blocks to sync each index up to the current tip are present. To make the process more efficient, we can fetch the oldest block from the indexers and perform the chain data existence check from that point only once. This also moves the pruning violation check to the end of the 'loadinit' thread, which is where the reindex, block loading and chain activation processes happen. Making the node's startup process faster, allowing us to remove the global g_indexes_ready_to_sync flag, and enabling the execution of the pruning violation verification even when the reindex or reindex-chainstate flags are enabled (which has being skipped so far).
1 parent fcbdaee commit ca91c24

File tree

6 files changed

+48
-37
lines changed

6 files changed

+48
-37
lines changed

src/index/base.cpp

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
#include <string>
2424
#include <utility>
2525

26-
using node::g_indexes_ready_to_sync;
27-
2826
constexpr uint8_t DB_BEST_BLOCK{'B'};
2927

3028
constexpr auto SYNC_LOG_INTERVAL{30s};
@@ -107,40 +105,16 @@ bool BaseIndex::Init()
107105
SetBestBlockIndex(locator_index);
108106
}
109107

110-
// Skip pruning check if indexes are not ready to sync (because reindex-chainstate has wiped the chain).
111-
const CBlockIndex* start_block = m_best_block_index.load();
112-
bool synced = start_block == active_chain.Tip();
113-
if (!synced && g_indexes_ready_to_sync) {
114-
const CBlockIndex* block_to_test = start_block ? start_block : active_chain.Genesis();
115-
116-
// Assert block_to_test is not null here. It can't be null because the
117-
// genesis block can't be null here. The genesis block will be null
118-
// during this BaseIndex::Init() call if the node is being started for
119-
// the first time, or if -reindex is used. But in both of these cases
120-
// m_best_block_index is also null so this branch is not reached.
121-
assert(block_to_test);
122-
123-
if (!active_chain.Contains(block_to_test)) {
124-
// if the bestblock is not part of the mainchain, find the fork
125-
// so we can make sure we have all data down to the fork
126-
block_to_test = active_chain.FindFork(block_to_test);
127-
}
128-
129-
// make sure we have all block data back to the start block
130-
if (!m_chainstate->m_blockman.CheckBlockDataAvailability(*active_chain.Tip(), *Assert(block_to_test))) {
131-
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
132-
}
133-
}
134-
135108
// Child init
109+
const CBlockIndex* start_block = m_best_block_index.load();
136110
if (!CustomInit(start_block ? std::make_optional(interfaces::BlockKey{start_block->GetBlockHash(), start_block->nHeight}) : std::nullopt)) {
137111
return false;
138112
}
139113

140114
// Note: this will latch to true immediately if the user starts up with an empty
141115
// datadir and an index enabled. If this is the case, indexation will happen solely
142116
// via `BlockConnected` signals until, possibly, the next restart.
143-
m_synced = synced;
117+
m_synced = start_block == active_chain.Tip();
144118
m_init = true;
145119
return true;
146120
}
@@ -412,7 +386,13 @@ IndexSummary BaseIndex::GetSummary() const
412386
IndexSummary summary{};
413387
summary.name = GetName();
414388
summary.synced = m_synced;
415-
summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0;
389+
if (const auto& pindex = m_best_block_index.load()) {
390+
summary.best_block_height = pindex->nHeight;
391+
summary.best_block_hash = pindex->GetBlockHash();
392+
} else {
393+
summary.best_block_height = 0;
394+
summary.best_block_hash = m_chain->getBlockHash(0);
395+
}
416396
return summary;
417397
}
418398

src/index/base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct IndexSummary {
2323
std::string name;
2424
bool synced{false};
2525
int best_block_height{0};
26+
uint256 best_block_hash;
2627
};
2728

2829
/**

src/init.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ using node::CalculateCacheSizes;
125125
using node::DEFAULT_PERSIST_MEMPOOL;
126126
using node::DEFAULT_PRINTPRIORITY;
127127
using node::fReindex;
128-
using node::g_indexes_ready_to_sync;
129128
using node::KernelNotifications;
130129
using node::LoadChainstate;
131130
using node::MempoolPath;
@@ -1545,8 +1544,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15451544

15461545
// ********************************************************* Step 8: start indexers
15471546

1548-
// If reindex-chainstate was specified, delay syncing indexes until ImportBlocks has reindexed the chain
1549-
if (!fReindexChainState) g_indexes_ready_to_sync = true;
15501547
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
15511548
auto result{WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db)))};
15521549
if (!result) {
@@ -1884,6 +1881,44 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
18841881

18851882
bool StartIndexBackgroundSync(NodeContext& node)
18861883
{
1884+
// Find the oldest block among all indexes.
1885+
// This block is used to verify that we have the required blocks' data stored on disk,
1886+
// starting from that point up to the current tip.
1887+
// indexes_start_block='nullptr' means "start from height 0".
1888+
std::optional<const CBlockIndex*> indexes_start_block;
1889+
std::string older_index_name;
1890+
1891+
ChainstateManager& chainman = *Assert(node.chainman);
1892+
for (auto index : node.indexes) {
1893+
const IndexSummary& summary = index->GetSummary();
1894+
if (summary.synced) continue;
1895+
1896+
// Get the last common block between the index best block and the active chain
1897+
LOCK(::cs_main);
1898+
const CChain& active_chain = chainman.ActiveChain();
1899+
const CBlockIndex* pindex = chainman.m_blockman.LookupBlockIndex(summary.best_block_hash);
1900+
if (!active_chain.Contains(pindex)) {
1901+
pindex = active_chain.FindFork(pindex);
1902+
}
1903+
1904+
if (!indexes_start_block || !pindex || pindex->nHeight < indexes_start_block.value()->nHeight) {
1905+
indexes_start_block = pindex;
1906+
older_index_name = summary.name;
1907+
if (!pindex) break; // Starting from genesis so no need to look for earlier block.
1908+
}
1909+
};
1910+
1911+
// Verify all blocks needed to sync to current tip are present.
1912+
if (indexes_start_block) {
1913+
LOCK(::cs_main);
1914+
const CBlockIndex* start_block = *indexes_start_block;
1915+
if (!start_block) start_block = chainman.ActiveChain().Genesis();
1916+
if (!chainman.m_blockman.CheckBlockDataAvailability(*chainman.ActiveChain().Tip(), *Assert(start_block))) {
1917+
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), older_index_name));
1918+
}
1919+
}
1920+
1921+
// Start threads
18871922
for (auto index : node.indexes) if (!index->StartBackgroundSync()) return false;
18881923
return true;
18891924
}

src/node/blockstorage.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
namespace node {
2828
std::atomic_bool fReindex(false);
29-
std::atomic_bool g_indexes_ready_to_sync{false};
3029

3130
bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
3231
{
@@ -954,7 +953,5 @@ void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFile
954953
return;
955954
}
956955
} // End scope of ImportingNow
957-
958-
g_indexes_ready_to_sync = true;
959956
}
960957
} // namespace node

src/node/blockstorage.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
5050
static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int);
5151

5252
extern std::atomic_bool fReindex;
53-
extern std::atomic_bool g_indexes_ready_to_sync;
5453

5554
// Because validation code takes pointers to the map's CBlockIndex objects, if
5655
// we ever switch to another associative container, we need to either use a

src/test/util/setup_common.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
157157
noui_connect();
158158
noui_connected = true;
159159
}
160-
node::g_indexes_ready_to_sync = true;
161160
}
162161

163162
BasicTestingSetup::~BasicTestingSetup()

0 commit comments

Comments
 (0)