mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-23 07:15:29 +01:00
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 <luke-jr+git@utopios.org>
This commit is contained in:
parent
231fc7b035
commit
2561823531
3 changed files with 60 additions and 3 deletions
|
@ -21,6 +21,8 @@
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
std::atomic_bool fImporting(false);
|
std::atomic_bool fImporting(false);
|
||||||
std::atomic_bool fReindex(false);
|
std::atomic_bool fReindex(false);
|
||||||
|
@ -230,6 +232,11 @@ void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPr
|
||||||
nLastBlockWeCanPrune, count);
|
nLastBlockWeCanPrune, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) {
|
||||||
|
AssertLockHeld(::cs_main);
|
||||||
|
m_prune_locks[name] = lock_info;
|
||||||
|
}
|
||||||
|
|
||||||
CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(cs_main);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
extern RecursiveMutex cs_main;
|
extern RecursiveMutex cs_main;
|
||||||
|
@ -65,6 +66,10 @@ struct CBlockIndexHeightOnlyComparator {
|
||||||
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
|
bool operator()(const CBlockIndex* pa, const CBlockIndex* pb) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PruneLockInfo {
|
||||||
|
int height_first{std::numeric_limits<int>::max()}; //! Height of earliest block that should be kept and not pruned
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
|
* Maintains a tree of blocks (stored in `m_block_index`) which is consulted
|
||||||
* to determine where the most-work tip is.
|
* to determine where the most-work tip is.
|
||||||
|
@ -118,6 +123,14 @@ private:
|
||||||
/** Dirty block file entries. */
|
/** Dirty block file entries. */
|
||||||
std::set<int> m_dirty_fileinfo;
|
std::set<int> m_dirty_fileinfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map from external index name to oldest block that must not be pruned.
|
||||||
|
*
|
||||||
|
* @note Internally, only blocks at height (height_first - PRUNE_LOCK_BUFFER - 1) and
|
||||||
|
* below will be pruned, but callers should avoid assuming any particular buffer size.
|
||||||
|
*/
|
||||||
|
std::unordered_map<std::string, PruneLockInfo> m_prune_locks GUARDED_BY(::cs_main);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BlockMap m_block_index GUARDED_BY(cs_main);
|
BlockMap m_block_index GUARDED_BY(cs_main);
|
||||||
|
|
||||||
|
@ -175,6 +188,9 @@ public:
|
||||||
//! Check whether the block associated with this index entry is pruned or not.
|
//! Check whether the block associated with this index entry is pruned or not.
|
||||||
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
|
//! Create or update a prune lock identified by its name
|
||||||
|
void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||||
|
|
||||||
~BlockManager()
|
~BlockManager()
|
||||||
{
|
{
|
||||||
Unload();
|
Unload();
|
||||||
|
|
|
@ -106,6 +106,12 @@ const std::vector<std::string> CHECKLEVEL_DOC {
|
||||||
"level 4 tries to reconnect the blocks",
|
"level 4 tries to reconnect the blocks",
|
||||||
"each level includes the checks of the previous levels",
|
"each level includes the checks of the previous levels",
|
||||||
};
|
};
|
||||||
|
/** The number of blocks to keep below the deepest prune lock.
|
||||||
|
* There is nothing special about this number. It is higher than what we
|
||||||
|
* expect to see in regular mainnet reorgs, but not so high that it would
|
||||||
|
* noticeably interfere with the pruning mechanism.
|
||||||
|
* */
|
||||||
|
static constexpr int PRUNE_LOCK_BUFFER{10};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mutex to guard access to validation specific variables, such as reading
|
* Mutex to guard access to validation specific variables, such as reading
|
||||||
|
@ -2338,13 +2344,29 @@ bool CChainState::FlushStateToDisk(
|
||||||
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
|
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
|
||||||
LOCK(m_blockman.cs_LastBlockFile);
|
LOCK(m_blockman.cs_LastBlockFile);
|
||||||
if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
|
if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
|
||||||
// make sure we don't prune above the blockfilterindexes bestblocks
|
// make sure we don't prune above any of the prune locks bestblocks
|
||||||
// pruning is height-based
|
// pruning is height-based
|
||||||
int last_prune = m_chain.Height(); // last height we can prune
|
int last_prune{m_chain.Height()}; // last height we can prune
|
||||||
|
std::optional<std::string> limiting_lock; // prune lock that actually was the limiting factor, only used for logging
|
||||||
|
|
||||||
ForEachBlockFilterIndex([&](BlockFilterIndex& index) {
|
ForEachBlockFilterIndex([&](BlockFilterIndex& index) {
|
||||||
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
|
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (const auto& prune_lock : m_blockman.m_prune_locks) {
|
||||||
|
if (prune_lock.second.height_first == std::numeric_limits<int>::max()) continue;
|
||||||
|
// Remove the buffer and one additional block here to get actual height that is outside of the buffer
|
||||||
|
const int lock_height{prune_lock.second.height_first - PRUNE_LOCK_BUFFER - 1};
|
||||||
|
last_prune = std::max(1, std::min(last_prune, lock_height));
|
||||||
|
if (last_prune == lock_height) {
|
||||||
|
limiting_lock = prune_lock.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limiting_lock) {
|
||||||
|
LogPrint(BCLog::PRUNE, "%s limited pruning to height %d\n", limiting_lock.value(), last_prune);
|
||||||
|
}
|
||||||
|
|
||||||
if (nManualPruneHeight > 0) {
|
if (nManualPruneHeight > 0) {
|
||||||
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
|
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
|
||||||
|
|
||||||
|
@ -2581,6 +2603,18 @@ bool CChainState::DisconnectTip(BlockValidationState& state, DisconnectedBlockTr
|
||||||
assert(flushed);
|
assert(flushed);
|
||||||
}
|
}
|
||||||
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
|
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Prune locks that began at or after the tip should be moved backward so they get a chance to reorg
|
||||||
|
const int max_height_first{pindexDelete->nHeight - 1};
|
||||||
|
for (auto& prune_lock : m_blockman.m_prune_locks) {
|
||||||
|
if (prune_lock.second.height_first <= max_height_first) continue;
|
||||||
|
|
||||||
|
prune_lock.second.height_first = max_height_first;
|
||||||
|
LogPrint(BCLog::PRUNE, "%s prune lock moved back to %d\n", prune_lock.first, max_height_first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write the chain state to disk, if necessary.
|
// Write the chain state to disk, if necessary.
|
||||||
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
|
if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Add table
Reference in a new issue