mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 15:09:59 +01:00
coins: add Sync() method to allow flush without cacheCoins drop
In certain circumstances, we may want to flush to disk without emptying `cacheCoins`, which affects performance. UTXO snapshot activation is one such case. This method is currently unused and this commit does not change any behavior. Incorporates feedback from John Newbery. Co-authored-by: Suhas Daftuar <sdaftuar@gmail.com>
This commit is contained in:
committed by
James O'Beirne
parent
c0b6c40bb0
commit
79cedc36af
@@ -13,7 +13,7 @@
|
|||||||
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
|
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
|
||||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||||
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
|
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
|
||||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return false; }
|
||||||
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
|
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
|
||||||
|
|
||||||
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
||||||
@@ -28,7 +28,7 @@ bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->
|
|||||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||||
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
|
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
|
||||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return base->BatchWrite(mapCoins, hashBlock, erase); }
|
||||||
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
|
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
|
||||||
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
|
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
|
||||||
|
|
||||||
@@ -176,8 +176,10 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
|||||||
hashBlock = hashBlockIn;
|
hashBlock = hashBlockIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
|
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool erase) {
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
|
for (CCoinsMap::iterator it = mapCoins.begin();
|
||||||
|
it != mapCoins.end();
|
||||||
|
it = erase ? mapCoins.erase(it) : std::next(it)) {
|
||||||
// Ignore non-dirty entries (optimization).
|
// Ignore non-dirty entries (optimization).
|
||||||
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -190,7 +192,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
|||||||
// Create the coin in the parent cache, move the data up
|
// Create the coin in the parent cache, move the data up
|
||||||
// and mark it as dirty.
|
// and mark it as dirty.
|
||||||
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
||||||
entry.coin = std::move(it->second.coin);
|
if (erase) {
|
||||||
|
// The `move` call here is purely an optimization; we rely on the
|
||||||
|
// `mapCoins.erase` call in the `for` expression to actually remove
|
||||||
|
// the entry from the child map.
|
||||||
|
entry.coin = std::move(it->second.coin);
|
||||||
|
} else {
|
||||||
|
entry.coin = it->second.coin;
|
||||||
|
}
|
||||||
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
|
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
|
||||||
entry.flags = CCoinsCacheEntry::DIRTY;
|
entry.flags = CCoinsCacheEntry::DIRTY;
|
||||||
// We can mark it FRESH in the parent if it was FRESH in the child
|
// We can mark it FRESH in the parent if it was FRESH in the child
|
||||||
@@ -218,7 +227,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
|||||||
} else {
|
} else {
|
||||||
// A normal modification.
|
// A normal modification.
|
||||||
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
|
||||||
itUs->second.coin = std::move(it->second.coin);
|
if (erase) {
|
||||||
|
// The `move` call here is purely an optimization; we rely on the
|
||||||
|
// `mapCoins.erase` call in the `for` expression to actually remove
|
||||||
|
// the entry from the child map.
|
||||||
|
itUs->second.coin = std::move(it->second.coin);
|
||||||
|
} else {
|
||||||
|
itUs->second.coin = it->second.coin;
|
||||||
|
}
|
||||||
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
|
||||||
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
||||||
// NOTE: It isn't safe to mark the coin as FRESH in the parent
|
// NOTE: It isn't safe to mark the coin as FRESH in the parent
|
||||||
@@ -233,12 +249,29 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::Flush() {
|
bool CCoinsViewCache::Flush() {
|
||||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
|
bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/ true);
|
||||||
cacheCoins.clear();
|
cacheCoins.clear();
|
||||||
cachedCoinsUsage = 0;
|
cachedCoinsUsage = 0;
|
||||||
return fOk;
|
return fOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CCoinsViewCache::Sync()
|
||||||
|
{
|
||||||
|
bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/ false);
|
||||||
|
// Instead of clearing `cacheCoins` as we would in Flush(), just clear the
|
||||||
|
// FRESH/DIRTY flags of any coin that isn't spent.
|
||||||
|
for (auto it = cacheCoins.begin(); it != cacheCoins.end(); ) {
|
||||||
|
if (it->second.coin.IsSpent()) {
|
||||||
|
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
|
||||||
|
it = cacheCoins.erase(it);
|
||||||
|
} else {
|
||||||
|
it->second.flags = 0;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fOk;
|
||||||
|
}
|
||||||
|
|
||||||
void CCoinsViewCache::Uncache(const COutPoint& hash)
|
void CCoinsViewCache::Uncache(const COutPoint& hash)
|
||||||
{
|
{
|
||||||
CCoinsMap::iterator it = cacheCoins.find(hash);
|
CCoinsMap::iterator it = cacheCoins.find(hash);
|
||||||
|
|||||||
20
src/coins.h
20
src/coins.h
@@ -176,7 +176,7 @@ public:
|
|||||||
|
|
||||||
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
||||||
//! The passed mapCoins can be modified.
|
//! The passed mapCoins can be modified.
|
||||||
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true);
|
||||||
|
|
||||||
//! Get a cursor to iterate over the whole state
|
//! Get a cursor to iterate over the whole state
|
||||||
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
|
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
|
||||||
@@ -202,7 +202,7 @@ public:
|
|||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
std::vector<uint256> GetHeadBlocks() const override;
|
std::vector<uint256> GetHeadBlocks() const override;
|
||||||
void SetBackend(CCoinsView &viewIn);
|
void SetBackend(CCoinsView &viewIn);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
|
||||||
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
|
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
|
||||||
size_t EstimateSize() const override;
|
size_t EstimateSize() const override;
|
||||||
};
|
};
|
||||||
@@ -235,7 +235,7 @@ public:
|
|||||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
void SetBestBlock(const uint256 &hashBlock);
|
void SetBestBlock(const uint256 &hashBlock);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
|
||||||
std::unique_ptr<CCoinsViewCursor> Cursor() const override {
|
std::unique_ptr<CCoinsViewCursor> Cursor() const override {
|
||||||
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
|
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
|
||||||
}
|
}
|
||||||
@@ -282,12 +282,22 @@ public:
|
|||||||
bool SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr);
|
bool SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push the modifications applied to this cache to its base.
|
* Push the modifications applied to this cache to its base and wipe local state.
|
||||||
* Failure to call this method before destruction will cause the changes to be forgotten.
|
* Failure to call this method or Sync() before destruction will cause the changes
|
||||||
|
* to be forgotten.
|
||||||
* If false is returned, the state of this cache (and its backing view) will be undefined.
|
* If false is returned, the state of this cache (and its backing view) will be undefined.
|
||||||
*/
|
*/
|
||||||
bool Flush();
|
bool Flush();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push the modifications applied to this cache to its base while retaining
|
||||||
|
* the contents of this cache (except for spent coins, which we erase).
|
||||||
|
* Failure to call this method or Flush() before destruction will cause the changes
|
||||||
|
* to be forgotten.
|
||||||
|
* If false is returned, the state of this cache (and its backing view) will be undefined.
|
||||||
|
*/
|
||||||
|
bool Sync();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the UTXO with the given outpoint from the cache, if it is
|
* Removes the UTXO with the given outpoint from the cache, if it is
|
||||||
* not modified.
|
* not modified.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public:
|
|||||||
|
|
||||||
uint256 GetBestBlock() const override { return hashBestBlock_; }
|
uint256 GetBestBlock() const override { return hashBestBlock_; }
|
||||||
|
|
||||||
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override
|
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool erase = true) override
|
||||||
{
|
{
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
|
|||||||
return vhashHeadBlocks;
|
return vhashHeadBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) {
|
||||||
CDBBatch batch(*m_db);
|
CDBBatch batch(*m_db);
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
size_t changed = 0;
|
size_t changed = 0;
|
||||||
@@ -146,8 +146,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
|||||||
changed++;
|
changed++;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
CCoinsMap::iterator itOld = it++;
|
it = erase ? mapCoins.erase(it) : std::next(it);
|
||||||
mapCoins.erase(itOld);
|
|
||||||
if (batch.SizeEstimate() > batch_size) {
|
if (batch.SizeEstimate() > batch_size) {
|
||||||
LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
|
LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
|
||||||
m_db->WriteBatch(batch);
|
m_db->WriteBatch(batch);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public:
|
|||||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
std::vector<uint256> GetHeadBlocks() const override;
|
std::vector<uint256> GetHeadBlocks() const override;
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
|
||||||
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
|
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
|
||||||
|
|
||||||
//! Whether an unsupported database format is used.
|
//! Whether an unsupported database format is used.
|
||||||
|
|||||||
Reference in New Issue
Block a user