Skip to content

Commit 4479eb0

Browse files
committed
Merge #18960: indexes: Add compact block filter headers cache
0187d4c [indexes] Add compact block filter headers cache (John Newbery) Pull request description: Cache block filter headers at heights of multiples of 1000 in memory. Block filter headers at height 1000x are checkpointed, and will be the most frequently requested. Cache them in memory to avoid costly disk reads. ACKs for top commit: jkczyz: ACK 0187d4c theStack: ACK 0187d4c 🎉 fjahr: re-utACK 0187d4c laanwj: code review ACK 0187d4c ariard: Code Review ACK 0187d4c. Tree-SHA512: 2075ae36901ebcdc4a217eae5203ebc8582181a0831fb7a53a119f031c46bca960a610a38a3d0636a9a405f713efcf4200c85f10c8559fd80139036d89473c56
2 parents fed1a90 + 0187d4c commit 4479eb0

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

src/index/blockfilterindex.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ constexpr char DB_FILTER_POS = 'P';
3131
constexpr unsigned int MAX_FLTR_FILE_SIZE = 0x1000000; // 16 MiB
3232
/** The pre-allocation chunk size for fltr?????.dat files */
3333
constexpr unsigned int FLTR_FILE_CHUNK_SIZE = 0x100000; // 1 MiB
34+
/** Maximum size of the cfheaders cache
35+
* We have a limit to prevent a bug in filling this cache
36+
* potentially turning into an OOM. At 2000 entries, this cache
37+
* is big enough for a 2,000,000 length block chain, which
38+
* we should be enough until ~2047. */
39+
constexpr size_t CF_HEADERS_CACHE_MAX_SZ{2000};
3440

3541
namespace {
3642

@@ -377,13 +383,32 @@ bool BlockFilterIndex::LookupFilter(const CBlockIndex* block_index, BlockFilter&
377383
return ReadFilterFromDisk(entry.pos, filter_out);
378384
}
379385

380-
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const
386+
bool BlockFilterIndex::LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out)
381387
{
388+
LOCK(m_cs_headers_cache);
389+
390+
bool is_checkpoint{block_index->nHeight % CFCHECKPT_INTERVAL == 0};
391+
392+
if (is_checkpoint) {
393+
// Try to find the block in the headers cache if this is a checkpoint height.
394+
auto header = m_headers_cache.find(block_index->GetBlockHash());
395+
if (header != m_headers_cache.end()) {
396+
header_out = header->second;
397+
return true;
398+
}
399+
}
400+
382401
DBVal entry;
383402
if (!LookupOne(*m_db, block_index, entry)) {
384403
return false;
385404
}
386405

406+
if (is_checkpoint &&
407+
m_headers_cache.size() < CF_HEADERS_CACHE_MAX_SZ) {
408+
// Add to the headers cache if this is a checkpoint height.
409+
m_headers_cache.emplace(block_index->GetBlockHash(), entry.header);
410+
}
411+
387412
header_out = entry.header;
388413
return true;
389414
}

src/index/blockfilterindex.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
#include <flatfile.h>
1111
#include <index/base.h>
1212

13+
/** Interval between compact filter checkpoints. See BIP 157. */
14+
static constexpr int CFCHECKPT_INTERVAL = 1000;
15+
16+
struct FilterHeaderHasher
17+
{
18+
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
19+
};
20+
1321
/**
1422
* BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of
1523
* blocks by height. An index is constructed for each supported filter type with its own database
@@ -30,6 +38,9 @@ class BlockFilterIndex final : public BaseIndex
3038
bool ReadFilterFromDisk(const FlatFilePos& pos, BlockFilter& filter) const;
3139
size_t WriteFilterToDisk(FlatFilePos& pos, const BlockFilter& filter);
3240

41+
Mutex m_cs_headers_cache;
42+
std::unordered_map<uint256, uint256, FilterHeaderHasher> m_headers_cache GUARDED_BY(m_cs_headers_cache);
43+
3344
protected:
3445
bool Init() override;
3546

@@ -54,7 +65,7 @@ class BlockFilterIndex final : public BaseIndex
5465
bool LookupFilter(const CBlockIndex* block_index, BlockFilter& filter_out) const;
5566

5667
/** Get a single filter header by block. */
57-
bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out) const;
68+
bool LookupFilterHeader(const CBlockIndex* block_index, uint256& header_out);
5869

5970
/** Get a range of filters between two heights on a chain. */
6071
bool LookupFilterRange(int start_height, const CBlockIndex* stop_index,

src/net_processing.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_
129129
static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60;
130130
/** Maximum feefilter broadcast delay after significant change. */
131131
static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60;
132-
/** Interval between compact filter checkpoints. See BIP 157. */
133-
static constexpr int CFCHECKPT_INTERVAL = 1000;
134132

135133
struct COrphanTx {
136134
// When modifying, adapt the copy of this definition in tests/DoS_tests.
@@ -1994,7 +1992,7 @@ static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_pa
19941992
BlockFilterType filter_type,
19951993
const uint256& stop_hash,
19961994
const CBlockIndex*& stop_index,
1997-
const BlockFilterIndex*& filter_index)
1995+
BlockFilterIndex*& filter_index)
19981996
{
19991997
const bool supported_filter_type =
20001998
(filter_type == BlockFilterType::BASIC &&
@@ -2049,7 +2047,7 @@ static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainPa
20492047
const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser);
20502048

20512049
const CBlockIndex* stop_index;
2052-
const BlockFilterIndex* filter_index;
2050+
BlockFilterIndex* filter_index;
20532051
if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, stop_hash,
20542052
stop_index, filter_index)) {
20552053
return;

0 commit comments

Comments
 (0)