Skip to content

Commit 79cedc3

Browse files
jamesobsdaftuar
andcommitted
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 <[email protected]>
1 parent c0b6c40 commit 79cedc3

File tree

5 files changed

+59
-17
lines changed

5 files changed

+59
-17
lines changed

src/coins.cpp

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
1414
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
1515
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
16-
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
16+
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return false; }
1717
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }
1818

1919
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
@@ -28,7 +28,7 @@ bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->
2828
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
2929
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
3030
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
31-
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
31+
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) { return base->BatchWrite(mapCoins, hashBlock, erase); }
3232
std::unique_ptr<CCoinsViewCursor> CCoinsViewBacked::Cursor() const { return base->Cursor(); }
3333
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
3434

@@ -176,8 +176,10 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
176176
hashBlock = hashBlockIn;
177177
}
178178

179-
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
180-
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
179+
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool erase) {
180+
for (CCoinsMap::iterator it = mapCoins.begin();
181+
it != mapCoins.end();
182+
it = erase ? mapCoins.erase(it) : std::next(it)) {
181183
// Ignore non-dirty entries (optimization).
182184
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
183185
continue;
@@ -190,7 +192,14 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
190192
// Create the coin in the parent cache, move the data up
191193
// and mark it as dirty.
192194
CCoinsCacheEntry& entry = cacheCoins[it->first];
193-
entry.coin = std::move(it->second.coin);
195+
if (erase) {
196+
// The `move` call here is purely an optimization; we rely on the
197+
// `mapCoins.erase` call in the `for` expression to actually remove
198+
// the entry from the child map.
199+
entry.coin = std::move(it->second.coin);
200+
} else {
201+
entry.coin = it->second.coin;
202+
}
194203
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
195204
entry.flags = CCoinsCacheEntry::DIRTY;
196205
// 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
218227
} else {
219228
// A normal modification.
220229
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
221-
itUs->second.coin = std::move(it->second.coin);
230+
if (erase) {
231+
// The `move` call here is purely an optimization; we rely on the
232+
// `mapCoins.erase` call in the `for` expression to actually remove
233+
// the entry from the child map.
234+
itUs->second.coin = std::move(it->second.coin);
235+
} else {
236+
itUs->second.coin = it->second.coin;
237+
}
222238
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
223239
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
224240
// 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
233249
}
234250

235251
bool CCoinsViewCache::Flush() {
236-
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
252+
bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/ true);
237253
cacheCoins.clear();
238254
cachedCoinsUsage = 0;
239255
return fOk;
240256
}
241257

258+
bool CCoinsViewCache::Sync()
259+
{
260+
bool fOk = base->BatchWrite(cacheCoins, hashBlock, /*erase=*/ false);
261+
// Instead of clearing `cacheCoins` as we would in Flush(), just clear the
262+
// FRESH/DIRTY flags of any coin that isn't spent.
263+
for (auto it = cacheCoins.begin(); it != cacheCoins.end(); ) {
264+
if (it->second.coin.IsSpent()) {
265+
cachedCoinsUsage -= it->second.coin.DynamicMemoryUsage();
266+
it = cacheCoins.erase(it);
267+
} else {
268+
it->second.flags = 0;
269+
++it;
270+
}
271+
}
272+
return fOk;
273+
}
274+
242275
void CCoinsViewCache::Uncache(const COutPoint& hash)
243276
{
244277
CCoinsMap::iterator it = cacheCoins.find(hash);

src/coins.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class CCoinsView
176176

177177
//! Do a bulk modification (multiple Coin changes + BestBlock change).
178178
//! The passed mapCoins can be modified.
179-
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
179+
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true);
180180

181181
//! Get a cursor to iterate over the whole state
182182
virtual std::unique_ptr<CCoinsViewCursor> Cursor() const;
@@ -202,7 +202,7 @@ class CCoinsViewBacked : public CCoinsView
202202
uint256 GetBestBlock() const override;
203203
std::vector<uint256> GetHeadBlocks() const override;
204204
void SetBackend(CCoinsView &viewIn);
205-
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
205+
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
206206
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
207207
size_t EstimateSize() const override;
208208
};
@@ -235,7 +235,7 @@ class CCoinsViewCache : public CCoinsViewBacked
235235
bool HaveCoin(const COutPoint &outpoint) const override;
236236
uint256 GetBestBlock() const override;
237237
void SetBestBlock(const uint256 &hashBlock);
238-
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
238+
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
239239
std::unique_ptr<CCoinsViewCursor> Cursor() const override {
240240
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
241241
}
@@ -282,12 +282,22 @@ class CCoinsViewCache : public CCoinsViewBacked
282282
bool SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr);
283283

284284
/**
285-
* Push the modifications applied to this cache to its base.
286-
* Failure to call this method before destruction will cause the changes to be forgotten.
285+
* Push the modifications applied to this cache to its base and wipe local state.
286+
* Failure to call this method or Sync() before destruction will cause the changes
287+
* to be forgotten.
287288
* If false is returned, the state of this cache (and its backing view) will be undefined.
288289
*/
289290
bool Flush();
290291

292+
/**
293+
* Push the modifications applied to this cache to its base while retaining
294+
* the contents of this cache (except for spent coins, which we erase).
295+
* Failure to call this method or Flush() before destruction will cause the changes
296+
* to be forgotten.
297+
* If false is returned, the state of this cache (and its backing view) will be undefined.
298+
*/
299+
bool Sync();
300+
291301
/**
292302
* Removes the UTXO with the given outpoint from the cache, if it is
293303
* not modified.

src/test/coins_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class CCoinsViewTest : public CCoinsView
5353

5454
uint256 GetBestBlock() const override { return hashBestBlock_; }
5555

56-
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override
56+
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool erase = true) override
5757
{
5858
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
5959
if (it->second.flags & CCoinsCacheEntry::DIRTY) {

src/txdb.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
111111
return vhashHeadBlocks;
112112
}
113113

114-
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
114+
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase) {
115115
CDBBatch batch(*m_db);
116116
size_t count = 0;
117117
size_t changed = 0;
@@ -146,8 +146,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
146146
changed++;
147147
}
148148
count++;
149-
CCoinsMap::iterator itOld = it++;
150-
mapCoins.erase(itOld);
149+
it = erase ? mapCoins.erase(it) : std::next(it);
151150
if (batch.SizeEstimate() > batch_size) {
152151
LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
153152
m_db->WriteBatch(batch);

src/txdb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class CCoinsViewDB final : public CCoinsView
6262
bool HaveCoin(const COutPoint &outpoint) const override;
6363
uint256 GetBestBlock() const override;
6464
std::vector<uint256> GetHeadBlocks() const override;
65-
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
65+
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool erase = true) override;
6666
std::unique_ptr<CCoinsViewCursor> Cursor() const override;
6767

6868
//! Whether an unsupported database format is used.

0 commit comments

Comments
 (0)