mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-25 14:28:53 +02:00
Merge bitcoin/bitcoin#31490: refactor: inline UndoWriteToDisk and WriteBlockToDisk to reduce serialization calls
223081ece6scripted-diff: rename block and undo functions for consistency (Lőrinc)baaa3b2846refactor,blocks: remove costly asserts and modernize affected logs (Lőrinc)fa39f27a0frefactor,blocks: deduplicate block's serialized size calculations (Lőrinc)dfb2f9d004refactor,blocks: inline `WriteBlockToDisk` (Lőrinc)42bc491465refactor,blocks: inline `UndoWriteToDisk` (Lőrinc)86b85bb11fbench: add SaveBlockBench (Lőrinc)34f9a0157arefactor,bench: rename bench/readblock.cpp to bench/readwriteblock.cpp (Lőrinc) Pull request description: `UndoWriteToDisk` and `WriteBlockToDisk` were delegating a subset of their functionality to single-use methods that didn't optimally capture a meaningful chunk of the algorithm, resulting in calculating things twice (serialized size, header size). This change inlines the awkward methods (asserting that all previous behavior was retained), and in separate commits makes the usages less confusing. Besides making the methods slightly more intuitive, the refactorings reduce duplicate calculations as well. The speed difference is insignificant for now (~0.5% for the new `SaveBlockToDiskBench`), but are a cleanup for follow-ups such as https://github.com/bitcoin/bitcoin/pull/31539 ACKs for top commit: ryanofsky: Code review ACK223081ece6. Since last review, "Save" was renamed to "Write", uint32_t references were dropped, some log statements and comments were improved as suggested, and a lot of tweaks made to commits and commit messages which should make this easier to review. hodlinator: ACK223081ece6TheCharlatan: ACK223081ece6andrewtoth: ACK223081ece6Tree-SHA512: 951bc8ad3504c510988afd95c561e3e259c6212bd14f6536fe56e8eb5bf5c35c32a368bbdb1d5aea1acc473d7e5bd9cdcde02008a148b05af1f955e413062d5c
This commit is contained in:
@@ -669,41 +669,14 @@ CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
|
||||
return &m_blockfile_info.at(n);
|
||||
}
|
||||
|
||||
bool BlockManager::UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock) const
|
||||
{
|
||||
// Open history file to append
|
||||
AutoFile fileout{OpenUndoFile(pos)};
|
||||
if (fileout.IsNull()) {
|
||||
LogError("%s: OpenUndoFile failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write index header
|
||||
unsigned int nSize = GetSerializeSize(blockundo);
|
||||
fileout << GetParams().MessageStart() << nSize;
|
||||
|
||||
// Write undo data
|
||||
long fileOutPos = fileout.tell();
|
||||
pos.nPos = (unsigned int)fileOutPos;
|
||||
fileout << blockundo;
|
||||
|
||||
// calculate & write checksum
|
||||
HashWriter hasher{};
|
||||
hasher << hashBlock;
|
||||
hasher << blockundo;
|
||||
fileout << hasher.GetHash();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& index) const
|
||||
bool BlockManager::ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const
|
||||
{
|
||||
const FlatFilePos pos{WITH_LOCK(::cs_main, return index.GetUndoPos())};
|
||||
|
||||
// Open history file to read
|
||||
AutoFile filein{OpenUndoFile(pos, true)};
|
||||
if (filein.IsNull()) {
|
||||
LogError("%s: OpenUndoFile failed for %s\n", __func__, pos.ToString());
|
||||
LogError("OpenUndoFile failed for %s", pos.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -963,28 +936,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const
|
||||
{
|
||||
// Open history file to append
|
||||
AutoFile fileout{OpenBlockFile(pos)};
|
||||
if (fileout.IsNull()) {
|
||||
LogError("%s: OpenBlockFile failed\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write index header
|
||||
unsigned int nSize = GetSerializeSize(TX_WITH_WITNESS(block));
|
||||
fileout << GetParams().MessageStart() << nSize;
|
||||
|
||||
// Write block
|
||||
long fileOutPos = fileout.tell();
|
||||
pos.nPos = (unsigned int)fileOutPos;
|
||||
fileout << TX_WITH_WITNESS(block);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||
bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
const BlockfileType type = BlockfileTypeForHeight(block.nHeight);
|
||||
@@ -992,33 +944,50 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid
|
||||
|
||||
// Write undo information to disk
|
||||
if (block.GetUndoPos().IsNull()) {
|
||||
FlatFilePos _pos;
|
||||
if (!FindUndoPos(state, block.nFile, _pos, ::GetSerializeSize(blockundo) + 40)) {
|
||||
LogError("%s: FindUndoPos failed\n", __func__);
|
||||
FlatFilePos pos;
|
||||
const unsigned int blockundo_size{static_cast<unsigned int>(GetSerializeSize(blockundo))};
|
||||
if (!FindUndoPos(state, block.nFile, pos, blockundo_size + UNDO_DATA_DISK_OVERHEAD)) {
|
||||
LogError("FindUndoPos failed");
|
||||
return false;
|
||||
}
|
||||
if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash())) {
|
||||
// Open history file to append
|
||||
AutoFile fileout{OpenUndoFile(pos)};
|
||||
if (fileout.IsNull()) {
|
||||
LogError("OpenUndoFile failed");
|
||||
return FatalError(m_opts.notifications, state, _("Failed to write undo data."));
|
||||
}
|
||||
|
||||
// Write index header
|
||||
fileout << GetParams().MessageStart() << blockundo_size;
|
||||
// Write undo data
|
||||
pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE;
|
||||
fileout << blockundo;
|
||||
|
||||
// Calculate & write checksum
|
||||
HashWriter hasher{};
|
||||
hasher << block.pprev->GetBlockHash();
|
||||
hasher << blockundo;
|
||||
fileout << hasher.GetHash();
|
||||
|
||||
// rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
|
||||
// we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
|
||||
// in the block file info as below; note that this does not catch the case where the undo writes are keeping up
|
||||
// with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
|
||||
// the FindNextBlockPos function
|
||||
if (_pos.nFile < cursor.file_num && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) {
|
||||
if (pos.nFile < cursor.file_num && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[pos.nFile].nHeightLast) {
|
||||
// Do not propagate the return code, a failed flush here should not
|
||||
// be an indication for a failed write. If it were propagated here,
|
||||
// the caller would assume the undo data not to be written, when in
|
||||
// fact it is. Note though, that a failed flush might leave the data
|
||||
// file untrimmed.
|
||||
if (!FlushUndoFile(_pos.nFile, true)) {
|
||||
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, "Failed to flush undo file %05i\n", _pos.nFile);
|
||||
if (!FlushUndoFile(pos.nFile, true)) {
|
||||
LogPrintLevel(BCLog::BLOCKSTORAGE, BCLog::Level::Warning, "Failed to flush undo file %05i\n", pos.nFile);
|
||||
}
|
||||
} else if (_pos.nFile == cursor.file_num && block.nHeight > cursor.undo_height) {
|
||||
} else if (pos.nFile == cursor.file_num && block.nHeight > cursor.undo_height) {
|
||||
cursor.undo_height = block.nHeight;
|
||||
}
|
||||
// update nUndoPos in block index
|
||||
block.nUndoPos = _pos.nPos;
|
||||
block.nUndoPos = pos.nPos;
|
||||
block.nStatus |= BLOCK_HAVE_UNDO;
|
||||
m_dirty_blockindex.insert(&block);
|
||||
}
|
||||
@@ -1026,7 +995,7 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const
|
||||
bool BlockManager::ReadBlock(CBlock& block, const FlatFilePos& pos) const
|
||||
{
|
||||
block.SetNull();
|
||||
|
||||
@@ -1060,11 +1029,11 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) const
|
||||
bool BlockManager::ReadBlock(CBlock& block, const CBlockIndex& index) const
|
||||
{
|
||||
const FlatFilePos block_pos{WITH_LOCK(cs_main, return index.GetBlockPos())};
|
||||
|
||||
if (!ReadBlockFromDisk(block, block_pos)) {
|
||||
if (!ReadBlock(block, block_pos)) {
|
||||
return false;
|
||||
}
|
||||
if (block.GetHash() != index.GetBlockHash()) {
|
||||
@@ -1074,7 +1043,7 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockManager::ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos) const
|
||||
bool BlockManager::ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos& pos) const
|
||||
{
|
||||
FlatFilePos hpos = pos;
|
||||
// If nPos is less than 8 the pos is null and we don't have the block data
|
||||
@@ -1119,22 +1088,27 @@ bool BlockManager::ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatF
|
||||
return true;
|
||||
}
|
||||
|
||||
FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight)
|
||||
FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
|
||||
{
|
||||
unsigned int nBlockSize = ::GetSerializeSize(TX_WITH_WITNESS(block));
|
||||
// Account for the 4 magic message start bytes + the 4 length bytes (8 bytes total,
|
||||
// defined as BLOCK_SERIALIZATION_HEADER_SIZE)
|
||||
nBlockSize += static_cast<unsigned int>(BLOCK_SERIALIZATION_HEADER_SIZE);
|
||||
FlatFilePos blockPos{FindNextBlockPos(nBlockSize, nHeight, block.GetBlockTime())};
|
||||
if (blockPos.IsNull()) {
|
||||
LogError("%s: FindNextBlockPos failed\n", __func__);
|
||||
const unsigned int block_size{static_cast<unsigned int>(GetSerializeSize(TX_WITH_WITNESS(block)))};
|
||||
FlatFilePos pos{FindNextBlockPos(block_size + BLOCK_SERIALIZATION_HEADER_SIZE, nHeight, block.GetBlockTime())};
|
||||
if (pos.IsNull()) {
|
||||
LogError("FindNextBlockPos failed");
|
||||
return FlatFilePos();
|
||||
}
|
||||
if (!WriteBlockToDisk(block, blockPos)) {
|
||||
AutoFile fileout{OpenBlockFile(pos)};
|
||||
if (fileout.IsNull()) {
|
||||
LogError("OpenBlockFile failed");
|
||||
m_opts.notifications.fatalError(_("Failed to write block."));
|
||||
return FlatFilePos();
|
||||
}
|
||||
return blockPos;
|
||||
|
||||
// Write index header
|
||||
fileout << GetParams().MessageStart() << block_size;
|
||||
// Write block
|
||||
pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE;
|
||||
fileout << TX_WITH_WITNESS(block);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
|
||||
|
||||
@@ -74,8 +74,11 @@ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
|
||||
/** The maximum size of a blk?????.dat file (since 0.8) */
|
||||
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
||||
|
||||
/** Size of header written by WriteBlockToDisk before a serialized CBlock */
|
||||
static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = std::tuple_size_v<MessageStartChars> + sizeof(unsigned int);
|
||||
/** Size of header written by WriteBlock before a serialized CBlock (8 bytes) */
|
||||
static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE{std::tuple_size_v<MessageStartChars> + sizeof(unsigned int)};
|
||||
|
||||
/** Total overhead when writing undo data: header (8 bytes) plus checksum (32 bytes) */
|
||||
static constexpr size_t UNDO_DATA_DISK_OVERHEAD{BLOCK_SERIALIZATION_HEADER_SIZE + uint256::size()};
|
||||
|
||||
// Because validation code takes pointers to the map's CBlockIndex objects, if
|
||||
// we ever switch to another associative container, we need to either use a
|
||||
@@ -161,7 +164,7 @@ private:
|
||||
* blockfile info, and checks if there is enough disk space to save the block.
|
||||
*
|
||||
* The nAddSize argument passed to this function should include not just the size of the serialized CBlock, but also the size of
|
||||
* separator fields which are written before it by WriteBlockToDisk (BLOCK_SERIALIZATION_HEADER_SIZE).
|
||||
* separator fields (BLOCK_SERIALIZATION_HEADER_SIZE).
|
||||
*/
|
||||
[[nodiscard]] FlatFilePos FindNextBlockPos(unsigned int nAddSize, unsigned int nHeight, uint64_t nTime);
|
||||
[[nodiscard]] bool FlushChainstateBlockFile(int tip_height);
|
||||
@@ -169,15 +172,6 @@ private:
|
||||
|
||||
AutoFile OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false) const;
|
||||
|
||||
/**
|
||||
* Write a block to disk. The pos argument passed to this function is modified by this call. Before this call, it should
|
||||
* point to an unused file location where separator fields will be written, followed by the serialized CBlock data.
|
||||
* After this call, it will point to the beginning of the serialized CBlock data, after the separator fields
|
||||
* (BLOCK_SERIALIZATION_HEADER_SIZE)
|
||||
*/
|
||||
bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const;
|
||||
bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock) const;
|
||||
|
||||
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
|
||||
void FindFilesToPruneManual(
|
||||
std::set<int>& setFilesToPrune,
|
||||
@@ -330,7 +324,7 @@ public:
|
||||
/** Get block file info entry for one block file */
|
||||
CBlockFileInfo* GetBlockFileInfo(size_t n);
|
||||
|
||||
bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||
bool WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
||||
|
||||
/** Store block on disk and update block file statistics.
|
||||
@@ -341,14 +335,13 @@ public:
|
||||
* @returns in case of success, the position to which the block was written to
|
||||
* in case of an error, an empty FlatFilePos
|
||||
*/
|
||||
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight);
|
||||
FlatFilePos WriteBlock(const CBlock& block, int nHeight);
|
||||
|
||||
/** Update blockfile info while processing a block during reindex. The block must be available on disk.
|
||||
*
|
||||
* @param[in] block the block being processed
|
||||
* @param[in] nHeight the height of the block
|
||||
* @param[in] pos the position of the serialized CBlock on disk. This is the position returned
|
||||
* by WriteBlockToDisk pointing at the CBlock, not the separator fields before it
|
||||
* @param[in] pos the position of the serialized CBlock on disk
|
||||
*/
|
||||
void UpdateBlockInfo(const CBlock& block, unsigned int nHeight, const FlatFilePos& pos);
|
||||
|
||||
@@ -421,11 +414,11 @@ public:
|
||||
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune) const;
|
||||
|
||||
/** Functions for disk access for blocks */
|
||||
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) const;
|
||||
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) const;
|
||||
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos) const;
|
||||
bool ReadBlock(CBlock& block, const FlatFilePos& pos) const;
|
||||
bool ReadBlock(CBlock& block, const CBlockIndex& index) const;
|
||||
bool ReadRawBlock(std::vector<uint8_t>& block, const FlatFilePos& pos) const;
|
||||
|
||||
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& index) const;
|
||||
bool ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const;
|
||||
|
||||
void CleanupBlockRevFiles() const;
|
||||
};
|
||||
|
||||
@@ -442,7 +442,7 @@ bool FillBlock(const CBlockIndex* index, const FoundBlock& block, UniqueLock<Rec
|
||||
if (block.m_next_block) FillBlock(active[index->nHeight] == index ? active[index->nHeight + 1] : nullptr, *block.m_next_block, lock, active, blockman);
|
||||
if (block.m_data) {
|
||||
REVERSE_LOCK(lock);
|
||||
if (!blockman.ReadBlockFromDisk(*block.m_data, *index)) block.m_data->SetNull();
|
||||
if (!blockman.ReadBlock(*block.m_data, *index)) block.m_data->SetNull();
|
||||
}
|
||||
block.found = true;
|
||||
return true;
|
||||
|
||||
@@ -144,7 +144,7 @@ CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMe
|
||||
}
|
||||
if (block_index) {
|
||||
CBlock block;
|
||||
if (blockman.ReadBlockFromDisk(block, *block_index)) {
|
||||
if (blockman.ReadBlock(block, *block_index)) {
|
||||
for (const auto& tx : block.vtx) {
|
||||
if (tx->GetHash() == hash) {
|
||||
hashBlock = block_index->GetBlockHash();
|
||||
|
||||
Reference in New Issue
Block a user