Skip to content

Commit 2561823

Browse files
fjahrluke-jr
andcommitted
blockstorage: Add prune locks to BlockManager
This change also introduces an aditional buffer of 10 blocks (PRUNE_LOCK_BUFFER) that will not be pruned before the best block. Co-authored-by: Luke Dashjr <[email protected]>
1 parent 231fc7b commit 2561823

File tree

3 files changed

+60
-3
lines changed

3 files changed

+60
-3
lines changed

src/node/blockstorage.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <util/system.h>
2222
#include <validation.h>
2323

24+
#include <unordered_map>
25+
2426
namespace node {
2527
std::atomic_bool fImporting(false);
2628
std::atomic_bool fReindex(false);
@@ -230,6 +232,11 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
230232
nLastBlockWeCanPrune, count);
231233
}
232234

235+
void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) {
236+
AssertLockHeld(::cs_main);
237+
m_prune_locks[name] = lock_info;
238+
}
239+
233240
CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
234241
{
235242
AssertLockHeld(cs_main);

src/node/blockstorage.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <atomic>
1515
#include <cstdint>
16+
#include <unordered_map>
1617
#include <vector>
1718

1819
extern RecursiveMutex cs_main;
@@ -65,6 +66,10 @@ struct CBlockIndexHeightOnlyComparator {
6566
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
6667
};
6768

69+
struct PruneLockInfo {
70+
int height_first{std::numeric_limits<int>::max()}; //! Height of earliest block that should be kept and not pruned
71+
};
72+
6873
/**
6974
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
7075
* to determine where the most-work tip is.
@@ -118,6 +123,14 @@ class BlockManager
118123
/** Dirty block file entries. */
119124
std::set<int> m_dirty_fileinfo;
120125

126+
/**
127+
* Map from external index name to oldest block that must not be pruned.
128+
*
129+
* @note Internally, only blocks at height (height_first - PRUNE_LOCK_BUFFER - 1) and
130+
* below will be pruned, but callers should avoid assuming any particular buffer size.
131+
*/
132+
std::unordered_map<std::string, PruneLockInfo> m_prune_locks GUARDED_BY(::cs_main);
133+
121134
public:
122135
BlockMap m_block_index GUARDED_BY(cs_main);
123136

@@ -175,6 +188,9 @@ class BlockManager
175188
//! Check whether the block associated with this index entry is pruned or not.
176189
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
177190

191+
//! Create or update a prune lock identified by its name
192+
void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
193+
178194
~BlockManager()
179195
{
180196
Unload();

src/validation.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ const std::vector<std::string> CHECKLEVEL_DOC {
106106
"level 4 tries to reconnect the blocks",
107107
"each level includes the checks of the previous levels",
108108
};
109+
/** The number of blocks to keep below the deepest prune lock.
110+
* There is nothing special about this number. It is higher than what we
111+
* expect to see in regular mainnet reorgs, but not so high that it would
112+
* noticeably interfere with the pruning mechanism.
113+
* */
114+
static constexpr int PRUNE_LOCK_BUFFER{10};
109115

110116
/**
111117
* Mutex to guard access to validation specific variables, such as reading
@@ -2338,13 +2344,29 @@ bool CChainState::FlushStateToDisk(
23382344
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
23392345
LOCK(m_blockman.cs_LastBlockFile);
23402346
if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
2341-
// make sure we don't prune above the blockfilterindexes bestblocks
2347+
// make sure we don't prune above any of the prune locks bestblocks
23422348
// pruning is height-based
2343-
int last_prune = m_chain.Height(); // last height we can prune
2349+
int last_prune{m_chain.Height()}; // last height we can prune
2350+
std::optional<std::string> limiting_lock; // prune lock that actually was the limiting factor, only used for logging
2351+
23442352
ForEachBlockFilterIndex([&](BlockFilterIndex& index) {
2345-
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
2353+
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
23462354
});
23472355

2356+
for (const auto& prune_lock : m_blockman.m_prune_locks) {
2357+
if (prune_lock.second.height_first == std::numeric_limits<int>::max()) continue;
2358+
// Remove the buffer and one additional block here to get actual height that is outside of the buffer
2359+
const int lock_height{prune_lock.second.height_first - PRUNE_LOCK_BUFFER - 1};
2360+
last_prune = std::max(1, std::min(last_prune, lock_height));
2361+
if (last_prune == lock_height) {
2362+
limiting_lock = prune_lock.first;
2363+
}
2364+
}
2365+
2366+
if (limiting_lock) {
2367+
LogPrint(BCLog::PRUNE, "%s limited pruning to height %d\n", limiting_lock.value(), last_prune);
2368+
}
2369+
23482370
if (nManualPruneHeight > 0) {
23492371
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
23502372

@@ -2581,6 +2603,18 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
25812603
assert(flushed);
25822604
}
25832605
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
2606+
2607+
{
2608+
// Prune locks that began at or after the tip should be moved backward so they get a chance to reorg
2609+
const int max_height_first{pindexDelete->nHeight - 1};
2610+
for (auto& prune_lock : m_blockman.m_prune_locks) {
2611+
if (prune_lock.second.height_first <= max_height_first) continue;
2612+
2613+
prune_lock.second.height_first = max_height_first;
2614+
LogPrint(BCLog::PRUNE, "%s prune lock moved back to %d\n", prune_lock.first, max_height_first);
2615+
}
2616+
}
2617+
25842618
// Write the chain state to disk, if necessary.
25852619
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
25862620
return false;

0 commit comments

Comments
 (0)