blockstorage: return an error code from ReadRawBlock()
It will enable different error handling flows for different error types. Also, `ReadRawBlockBench` performance has decreased due to no longer reusing a vector with an unchanging capacity - mirroring our production code behavior. Co-authored-by: Hodlinator <172445034+hodlinator@users.noreply.github.com> Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
This commit is contained in:
1 parent
cca113f5b0
commit
f2fd1aa21c
7 files changed
+41
-36
No files matched your search
@@ -57,11 +57,9 @@ static void ReadRawBlockBench(benchmark::Bench& bench)
|
|||||||
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
const auto testing_setup{MakeNoLogFileContext<const TestingSetup>(ChainType::MAIN)};
|
||||||
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
auto& blockman{testing_setup->m_node.chainman->m_blockman};
|
||||||
const auto pos{blockman.WriteBlock(CreateTestBlock(), 413'567)};
|
const auto pos{blockman.WriteBlock(CreateTestBlock(), 413'567)};
|
||||||
std::vector<std::byte> block_data;
|
|
||||||
blockman.ReadRawBlock(block_data, pos); // warmup
|
|
||||||
bench.run([&] {
|
bench.run([&] {
|
||||||
const auto success{blockman.ReadRawBlock(block_data, pos)};
|
const auto res{blockman.ReadRawBlock(pos)};
|
||||||
assert(success);
|
assert(res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -1759,7 +1759,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||||||
g_zmq_notification_interface = CZMQNotificationInterface::Create(
|
g_zmq_notification_interface = CZMQNotificationInterface::Create(
|
||||||
[&chainman = node.chainman](std::vector<std::byte>& block, const CBlockIndex& index) {
|
[&chainman = node.chainman](std::vector<std::byte>& block, const CBlockIndex& index) {
|
||||||
assert(chainman);
|
assert(chainman);
|
||||||
return chainman->m_blockman.ReadRawBlock(block, WITH_LOCK(cs_main, return index.GetBlockPos()));
|
if (auto ret{chainman->m_blockman.ReadRawBlock(WITH_LOCK(cs_main, return index.GetBlockPos()))}) {
|
||||||
|
block = std::move(*ret);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (g_zmq_notification_interface) {
|
if (g_zmq_notification_interface) {
|
||||||
|
|||||||
@@ -2276,8 +2276,9 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|||||||
} else if (inv.IsMsgWitnessBlk()) {
|
} else if (inv.IsMsgWitnessBlk()) {
|
||||||
// Fast-path: in this case it is possible to serve the block directly from disk,
|
// Fast-path: in this case it is possible to serve the block directly from disk,
|
||||||
// as the network format matches the format on disk
|
// as the network format matches the format on disk
|
||||||
std::vector<std::byte> block_data;
|
if (const auto block_data{m_chainman.m_blockman.ReadRawBlock(block_pos)}) {
|
||||||
if (!m_chainman.m_blockman.ReadRawBlock(block_data, block_pos)) {
|
MakeAndPushMessage(pfrom, NetMsgType::BLOCK, std::span{*block_data});
|
||||||
|
} else {
|
||||||
if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {
|
if (WITH_LOCK(m_chainman.GetMutex(), return m_chainman.m_blockman.IsBlockPruned(*pindex))) {
|
||||||
LogDebug(BCLog::NET, "Block was pruned before it could be read, %s\n", pfrom.DisconnectMsg(fLogIPs));
|
LogDebug(BCLog::NET, "Block was pruned before it could be read, %s\n", pfrom.DisconnectMsg(fLogIPs));
|
||||||
} else {
|
} else {
|
||||||
@@ -2286,7 +2287,6 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|||||||
pfrom.fDisconnect = true;
|
pfrom.fDisconnect = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MakeAndPushMessage(pfrom, NetMsgType::BLOCK, std::span{block_data});
|
|
||||||
// Don't set pblock as we've sent the block
|
// Don't set pblock as we've sent the block
|
||||||
} else {
|
} else {
|
||||||
// Send block from disk
|
// Send block from disk
|
||||||
|
|||||||
+12
-13
@@ -1006,14 +1006,14 @@ bool BlockManager::ReadBlock(CBlock& block, const FlatFilePos& pos, const std::o
|
|||||||
block.SetNull();
|
block.SetNull();
|
||||||
|
|
||||||
// Open history file to read
|
// Open history file to read
|
||||||
std::vector<std::byte> block_data;
|
const auto block_data{ReadRawBlock(pos)};
|
||||||
if (!ReadRawBlock(block_data, pos)) {
|
if (!block_data) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Read block
|
// Read block
|
||||||
SpanReader{block_data} >> TX_WITH_WITNESS(block);
|
SpanReader{*block_data} >> TX_WITH_WITNESS(block);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogError("Deserialize or I/O error - %s at %s while reading block", e.what(), pos.ToString());
|
LogError("Deserialize or I/O error - %s at %s while reading block", e.what(), pos.ToString());
|
||||||
return false;
|
return false;
|
||||||
@@ -1048,19 +1048,19 @@ bool BlockManager::ReadBlock(CBlock& block, const CBlockIndex& index) const
|
|||||||
return ReadBlock(block, block_pos, index.GetBlockHash());
|
return ReadBlock(block, block_pos, index.GetBlockHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockManager::ReadRawBlock(std::vector<std::byte>& block, const FlatFilePos& pos) const
|
BlockManager::ReadRawBlockResult BlockManager::ReadRawBlock(const FlatFilePos& pos) const
|
||||||
{
|
{
|
||||||
if (pos.nPos < STORAGE_HEADER_BYTES) {
|
if (pos.nPos < STORAGE_HEADER_BYTES) {
|
||||||
// If nPos is less than STORAGE_HEADER_BYTES, we can't read the header that precedes the block data
|
// If nPos is less than STORAGE_HEADER_BYTES, we can't read the header that precedes the block data
|
||||||
// This would cause an unsigned integer underflow when trying to position the file cursor
|
// This would cause an unsigned integer underflow when trying to position the file cursor
|
||||||
// This can happen after pruning or default constructed positions
|
// This can happen after pruning or default constructed positions
|
||||||
LogError("Failed for %s while reading raw block storage header", pos.ToString());
|
LogError("Failed for %s while reading raw block storage header", pos.ToString());
|
||||||
return false;
|
return util::Unexpected{ReadRawError::IO};
|
||||||
}
|
}
|
||||||
AutoFile filein{OpenBlockFile({pos.nFile, pos.nPos - STORAGE_HEADER_BYTES}, /*fReadOnly=*/true)};
|
AutoFile filein{OpenBlockFile({pos.nFile, pos.nPos - STORAGE_HEADER_BYTES}, /*fReadOnly=*/true)};
|
||||||
if (filein.IsNull()) {
|
if (filein.IsNull()) {
|
||||||
LogError("OpenBlockFile failed for %s while reading raw block", pos.ToString());
|
LogError("OpenBlockFile failed for %s while reading raw block", pos.ToString());
|
||||||
return false;
|
return util::Unexpected{ReadRawError::IO};
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1072,23 +1072,22 @@ bool BlockManager::ReadRawBlock(std::vector<std::byte>& block, const FlatFilePos
|
|||||||
if (blk_start != GetParams().MessageStart()) {
|
if (blk_start != GetParams().MessageStart()) {
|
||||||
LogError("Block magic mismatch for %s: %s versus expected %s while reading raw block",
|
LogError("Block magic mismatch for %s: %s versus expected %s while reading raw block",
|
||||||
pos.ToString(), HexStr(blk_start), HexStr(GetParams().MessageStart()));
|
pos.ToString(), HexStr(blk_start), HexStr(GetParams().MessageStart()));
|
||||||
return false;
|
return util::Unexpected{ReadRawError::IO};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blk_size > MAX_SIZE) {
|
if (blk_size > MAX_SIZE) {
|
||||||
LogError("Block data is larger than maximum deserialization size for %s: %s versus %s while reading raw block",
|
LogError("Block data is larger than maximum deserialization size for %s: %s versus %s while reading raw block",
|
||||||
pos.ToString(), blk_size, MAX_SIZE);
|
pos.ToString(), blk_size, MAX_SIZE);
|
||||||
return false;
|
return util::Unexpected{ReadRawError::IO};
|
||||||
}
|
}
|
||||||
|
|
||||||
block.resize(blk_size); // Zeroing of memory is intentional here
|
std::vector<std::byte> data(blk_size); // Zeroing of memory is intentional here
|
||||||
filein.read(block);
|
filein.read(data);
|
||||||
|
return data;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());
|
LogError("Read from block file failed: %s for %s while reading raw block", e.what(), pos.ToString());
|
||||||
return false;
|
return util::Unexpected{ReadRawError::IO};
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
|
FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
#include <uint256.h>
|
#include <uint256.h>
|
||||||
|
#include <util/expected.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/hasher.h>
|
#include <util/hasher.h>
|
||||||
|
|
||||||
@@ -169,6 +170,9 @@ struct BlockfileCursor {
|
|||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const BlockfileCursor& cursor);
|
std::ostream& operator<<(std::ostream& os, const BlockfileCursor& cursor);
|
||||||
|
|
||||||
|
enum class ReadRawError {
|
||||||
|
IO,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@@ -302,6 +306,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using Options = kernel::BlockManagerOpts;
|
using Options = kernel::BlockManagerOpts;
|
||||||
|
using ReadRawBlockResult = util::Expected<std::vector<std::byte>, ReadRawError>;
|
||||||
|
|
||||||
explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);
|
explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);
|
||||||
|
|
||||||
@@ -455,7 +460,7 @@ public:
|
|||||||
/** Functions for disk access for blocks */
|
/** Functions for disk access for blocks */
|
||||||
bool ReadBlock(CBlock& block, const FlatFilePos& pos, const std::optional<uint256>& expected_hash) const;
|
bool ReadBlock(CBlock& block, const FlatFilePos& pos, const std::optional<uint256>& expected_hash) const;
|
||||||
bool ReadBlock(CBlock& block, const CBlockIndex& index) const;
|
bool ReadBlock(CBlock& block, const CBlockIndex& index) const;
|
||||||
bool ReadRawBlock(std::vector<std::byte>& block, const FlatFilePos& pos) const;
|
ReadRawBlockResult ReadRawBlock(const FlatFilePos& pos) const;
|
||||||
|
|
||||||
bool ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const;
|
bool ReadBlockUndo(CBlockUndo& blockundo, const CBlockIndex& index) const;
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -416,20 +416,23 @@ static bool rest_block(const std::any& context,
|
|||||||
pos = pblockindex->GetBlockPos();
|
pos = pblockindex->GetBlockPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> block_data{};
|
const auto block_data{chainman.m_blockman.ReadRawBlock(pos)};
|
||||||
if (!chainman.m_blockman.ReadRawBlock(block_data, pos)) {
|
if (!block_data) {
|
||||||
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
switch (block_data.error()) {
|
||||||
|
case node::ReadRawError::IO: return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "I/O error reading " + hashStr);
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (rf) {
|
switch (rf) {
|
||||||
case RESTResponseFormat::BINARY: {
|
case RESTResponseFormat::BINARY: {
|
||||||
req->WriteHeader("Content-Type", "application/octet-stream");
|
req->WriteHeader("Content-Type", "application/octet-stream");
|
||||||
req->WriteReply(HTTP_OK, block_data);
|
req->WriteReply(HTTP_OK, *block_data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RESTResponseFormat::HEX: {
|
case RESTResponseFormat::HEX: {
|
||||||
const std::string strHex{HexStr(block_data) + "\n"};
|
const std::string strHex{HexStr(*block_data) + "\n"};
|
||||||
req->WriteHeader("Content-Type", "text/plain");
|
req->WriteHeader("Content-Type", "text/plain");
|
||||||
req->WriteReply(HTTP_OK, strHex);
|
req->WriteReply(HTTP_OK, strHex);
|
||||||
return true;
|
return true;
|
||||||
@@ -437,7 +440,7 @@ static bool rest_block(const std::any& context,
|
|||||||
|
|
||||||
case RESTResponseFormat::JSON: {
|
case RESTResponseFormat::JSON: {
|
||||||
CBlock block{};
|
CBlock block{};
|
||||||
DataStream block_stream{block_data};
|
DataStream block_stream{*block_data};
|
||||||
block_stream >> TX_WITH_WITNESS(block);
|
block_stream >> TX_WITH_WITNESS(block);
|
||||||
UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);
|
UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, tx_verbosity, chainman.GetConsensus().powLimit);
|
||||||
std::string strJSON = objBlock.write() + "\n";
|
std::string strJSON = objBlock.write() + "\n";
|
||||||
|
|||||||
@@ -680,7 +680,6 @@ static CBlock GetBlockChecked(BlockManager& blockman, const CBlockIndex& blockin
|
|||||||
|
|
||||||
static std::vector<std::byte> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
static std::vector<std::byte> GetRawBlockChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
||||||
{
|
{
|
||||||
std::vector<std::byte> data{};
|
|
||||||
FlatFilePos pos{};
|
FlatFilePos pos{};
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
@@ -688,13 +687,10 @@ static std::vector<std::byte> GetRawBlockChecked(BlockManager& blockman, const C
|
|||||||
pos = blockindex.GetBlockPos();
|
pos = blockindex.GetBlockPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!blockman.ReadRawBlock(data, pos)) {
|
if (auto data{blockman.ReadRawBlock(pos)}) return std::move(*data);
|
||||||
// Block not found on disk. This shouldn't normally happen unless the block was
|
// Block not found on disk. This shouldn't normally happen unless the block was
|
||||||
// pruned right after we released the lock above.
|
// pruned right after we released the lock above.
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
|
throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk");
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
static CBlockUndo GetUndoChecked(BlockManager& blockman, const CBlockIndex& blockindex)
|
||||||
|
|||||||
Reference in new issue
Block a user