refactor: Delegate to LevelDB for CDBBatch size estimation

Serialized batch size can be queried via the underlying LevelDB implementation calling the native `leveldb::WriteBatch::ApproximateSize()`.

The previous manual calculation was added in e66dbde6d1 as part of https://github.com/bitcoin/bitcoin/pull/10195. At that time (April 2017), the version of LevelDB used by Bitcoin Core (and even the latest source) lacked a native function for this. LevelDB added this capability in 69e2bd224b, merged later that year.

The old manual estimation method (`SizeEstimate()`) is kept temporarily in this commit, and assertions are added in `txdb.cpp` to verify its results against `ApproximateSize()` during batch writes. This ensures the native function behaves as expected before removing the manual calculation in the subsequent commit.
This commit is contained in:
Lőrinc 2025-04-01 14:06:10 +02:00
parent 751077c6e2
commit 8b5e19d8b5
3 changed files with 28 additions and 3 deletions

View file

@ -200,6 +200,11 @@ void CDBBatch::EraseImpl(std::span<const std::byte> key)
size_estimate += 2 + (slKey.size() > 127) + slKey.size();
}
size_t CDBBatch::ApproximateSize() const
{
return m_impl_batch->batch.ApproximateSize();
}
struct LevelDBContext {
//! custom environment this database is using (may be nullptr in case of default environment)
leveldb::Env* penv;

View file

@ -119,7 +119,8 @@ public:
ssKey.clear();
}
size_t SizeEstimate() const { return size_estimate; }
size_t ApproximateSize() const;
size_t SizeEstimate() const { return size_estimate; } // TODO replace with ApproximateSize
};
class CDBIterator

View file

@ -113,24 +113,39 @@ bool CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &hashB
// transition from old_tip to hashBlock.
// A vector is used for future extensibility, as we may want to support
// interrupting after partial writes from multiple independent reorgs.
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Erase(DB_BEST_BLOCK);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
for (auto it{cursor.Begin()}; it != cursor.End();) {
if (it->second.IsDirty()) {
CoinEntry entry(&it->first);
if (it->second.coin.IsSpent())
if (it->second.coin.IsSpent()) {
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Erase(entry);
else
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
} else {
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Write(entry, it->second.coin);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
}
changed++;
}
count++;
it = cursor.NextAndMaybeErase(*it);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
if (batch.SizeEstimate() > m_options.batch_write_bytes) {
LogDebug(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
m_db->WriteBatch(batch);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Clear();
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
if (m_options.simulate_crash_ratio) {
static FastRandomContext rng;
if (rng.randrange(m_options.simulate_crash_ratio) == 0) {
@ -142,11 +157,15 @@ bool CCoinsViewDB::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &hashB
}
// In the last batch, mark the database as consistent with hashBlock again.
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Erase(DB_HEAD_BLOCKS);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
batch.Write(DB_BEST_BLOCK, hashBlock);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
LogDebug(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
bool ret = m_db->WriteBatch(batch);
assert(batch.ApproximateSize() == batch.SizeEstimate()); // TODO remove
LogDebug(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
return ret;
}