Skip to content

Commit

Permalink
infra: migrate ensure to the dynamic format for performance reasons (
Browse files Browse the repository at this point in the history
  • Loading branch information
JacekGlen authored Jan 24, 2024
1 parent 65b0d27 commit 8859a3f
Show file tree
Hide file tree
Showing 21 changed files with 149 additions and 94 deletions.
2 changes: 1 addition & 1 deletion cmd/capi/execute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ std::vector<SilkwormChainSnapshot> collect_all_snapshots(const SnapshotRepositor
transactions_snapshot_sequence.push_back(raw_transactions_snapshot);
} break;
default:
ensure(false, "unexpected snapshot type: " + std::string{magic_enum::enum_name(segment_file.type())});
ensure(false, [&]() { return "unexpected snapshot type: " + std::string{magic_enum::enum_name(segment_file.type())}; });
}
}

Expand Down
18 changes: 10 additions & 8 deletions cmd/dev/check_log_indices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,34 +141,36 @@ void check_address_index(BlockNum block_number, const evmc::address& log_address
// Transaction log address must be present in LogAddressIndex table
const auto log_address_key{db::log_address_key(log_address, block_number)};
const auto log_address_data{log_address_cursor->lower_bound(db::to_slice(log_address_key), false)};
ensure(log_address_data.done, "LogAddressIndex does not contain key " + to_hex(log_address_key));
ensure(log_address_data.done, [&]() { return "LogAddressIndex does not contain key " + to_hex(log_address_key); });

const auto [address_view, address_upper_bound_block] = db::split_log_address_key(log_address_data.key);
ensure(to_hex(address_view) == to_hex(log_address.bytes), "address mismatch in LogAddressIndex table: " + to_hex(address_view));
ensure(address_upper_bound_block >= block_number, "upper bound mismatch in LogAddressIndex table: " + to_hex(address_view));
const auto& address_view_ref = address_view;
ensure(to_hex(address_view) == to_hex(log_address.bytes), [&]() { return "address mismatch in LogAddressIndex table: " + to_hex(address_view_ref); });
ensure(address_upper_bound_block >= block_number, [&]() { return "upper bound mismatch in LogAddressIndex table: " + to_hex(address_view_ref); });

// Retrieved chunk of the address roaring bitmap must contain the transaction log block
const auto& log_address_value{log_address_data.value};
const auto address_bitmap_chunk{db::bitmap::parse32(log_address_value)};
ensure(address_bitmap_chunk.contains(static_cast<uint32_t>(block_number)),
"address bitmap chunk " + address_bitmap_chunk.toString() + " does not contain block " + std::to_string(block_number));
[&]() { return "address bitmap chunk " + address_bitmap_chunk.toString() + " does not contain block " + std::to_string(block_number); });
}

void check_topic_index(BlockNum block_number, const evmc::bytes32& log_topic, db::ROCursor* log_topic_cursor) {
// Each transaction log topic must be present in LogTopicIndex table
const auto log_topic_key{db::log_topic_key(log_topic, block_number)};
const auto log_topic_data{log_topic_cursor->lower_bound(db::to_slice(log_topic_key), false)};
ensure(log_topic_data.done, "LogTopicIndex does not contain key " + to_hex(log_topic_key));
ensure(log_topic_data.done, [&]() { return "LogTopicIndex does not contain key " + to_hex(log_topic_key); });

const auto [topic_view, topic_upper_bound_block] = db::split_log_topic_key(log_topic_data.key);
ensure(to_hex(topic_view) == to_hex(log_topic.bytes), "topic mismatch in LogTopicIndex table: " + to_hex(topic_view));
ensure(topic_upper_bound_block >= block_number, "upper bound mismatch in LogTopicIndex table: " + to_hex(topic_view));
const auto& topic_view_ref = topic_view;
ensure(to_hex(topic_view) == to_hex(log_topic.bytes), [&]() { return "topic mismatch in LogTopicIndex table: " + to_hex(topic_view_ref); });
ensure(topic_upper_bound_block >= block_number, [&]() { return "upper bound mismatch in LogTopicIndex table: " + to_hex(topic_view_ref); });

// Retrieved chunk of the topic roaring bitmap must contain the transaction log block
const auto& log_topic_value{log_topic_data.value};
const auto topic_bitmap_chunk{db::bitmap::parse32(log_topic_value)};
ensure(topic_bitmap_chunk.contains(static_cast<uint32_t>(block_number)),
"topic bitmap chunk " + topic_bitmap_chunk.toString() + " does not contain block " + std::to_string(block_number));
[&]() { return "topic bitmap chunk " + topic_bitmap_chunk.toString() + " does not contain block " + std::to_string(block_number); });
}

int main(int argc, char* argv[]) {
Expand Down
34 changes: 17 additions & 17 deletions cmd/dev/db_toolbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ void unwind(db::EnvConfig& config, BlockNum unwind_point, bool remove_blocks) {
const auto unwind_result{stage_pipeline.unwind(txn, unwind_point)};

ensure(unwind_result == stagedsync::Stage::Result::kSuccess,
"unwind failed: " + std::string{magic_enum::enum_name<stagedsync::Stage::Result>(unwind_result)});
[&]() { return "unwind failed: " + std::string{magic_enum::enum_name<stagedsync::Stage::Result>(unwind_result)}; });

std::cout << "\n Staged pipeline unwind up to block: " << unwind_point << " completed\n";

Expand Down Expand Up @@ -1106,11 +1106,11 @@ static size_t print_single_table_diff(db::ROCursor* cursor1, db::ROCursor* curso
}

static void print_table_diff(db::ROTxn& txn1, db::ROTxn& txn2, const DbTableInfo& table1, const DbTableInfo& table2, bool force_print = false) {
ensure(table1.name == table2.name, "name mismatch: " + table1.name + " vs " + table2.name);
ensure(table1.name == table2.name, [&]() { return "name mismatch: " + table1.name + " vs " + table2.name; });
ensure(table1.info.key_mode() == table2.info.key_mode(),
"key_mode mismatch: " + std::to_string(int(table1.info.key_mode())) + " vs " + std::to_string(int(table2.info.key_mode())));
[&]() { return "key_mode mismatch: " + std::to_string(int(table1.info.key_mode())) + " vs " + std::to_string(int(table2.info.key_mode())); });
ensure(table1.info.value_mode() == table2.info.value_mode(),
"value_mode mismatch: " + std::to_string(int(table1.info.value_mode())) + " vs " + std::to_string(int(table2.info.value_mode())));
[&]() { return "value_mode mismatch: " + std::to_string(int(table1.info.value_mode())) + " vs " + std::to_string(int(table2.info.value_mode())); });

db::MapConfig table1_config{
.name = table1.name.c_str(),
Expand Down Expand Up @@ -1204,8 +1204,8 @@ static DbComparisonResult compare_db_content(db::ROTxn& txn1, db::ROTxn& txn2, c
}

void compare(db::EnvConfig& config, const fs::path& target_datadir_path, bool check_layout, bool verbose, std::optional<std::string_view> table) {
ensure(fs::exists(target_datadir_path), "target datadir " + target_datadir_path.string() + " does not exist");
ensure(fs::is_directory(target_datadir_path), "target datadir " + target_datadir_path.string() + " must be a folder");
ensure(fs::exists(target_datadir_path), [&]() { return "target datadir " + target_datadir_path.string() + " does not exist"; });
ensure(fs::is_directory(target_datadir_path), [&]() { return "target datadir " + target_datadir_path.string() + " must be a folder"; });

DataDirectory target_datadir{target_datadir_path};
db::EnvConfig target_config{target_datadir.chaindata().path()};
Expand Down Expand Up @@ -1327,10 +1327,10 @@ void print_canonical_blocks(db::EnvConfig& config, BlockNum from, std::optional<
// Use last block as max block if to is missing and perform range checks
BlockNum last{db::block_number_from_key(last_data.key)};
if (to) {
ensure(from <= *to, "Block from=" + std::to_string(from) + " must not be greater than to=" + std::to_string(*to));
ensure(*to <= last, "Block to=" + std::to_string(*to) + " must not be greater than last=" + std::to_string(last));
ensure(from <= *to, [&]() { return "Block from=" + std::to_string(from) + " must not be greater than to=" + std::to_string(*to); });
ensure(*to <= last, [&]() { return "Block to=" + std::to_string(*to) + " must not be greater than last=" + std::to_string(last); });
} else {
ensure(from <= last, "Block from=" + std::to_string(from) + " must not be greater than last=" + std::to_string(last));
ensure(from <= last, [&]() { return "Block from=" + std::to_string(from) + " must not be greater than last=" + std::to_string(last); });
to = last;
}

Expand All @@ -1341,17 +1341,17 @@ void print_canonical_blocks(db::EnvConfig& config, BlockNum from, std::optional<
// Lookup each canonical block hash from each block number
auto block_number_key{db::block_key(block_number)};
auto ch_data{canonical_hashes_table->find(db::to_slice(block_number_key), /*throw_notfound=*/false)};
ensure(ch_data.done, "Table CanonicalHashes does not contain key=" + to_hex(block_number_key));
ensure(ch_data.done, [&]() { return "Table CanonicalHashes does not contain key=" + to_hex(block_number_key); });
const auto block_hash{to_bytes32(db::from_slice(ch_data.value))};

// Read and decode each canonical block header
auto block_key{db::block_key(block_number, block_hash.bytes)};
auto bh_data{block_headers_table->find(db::to_slice(block_key), /*throw_notfound=*/false)};
ensure(bh_data.done, "Table Headers does not contain key=" + to_hex(block_key));
ensure(bh_data.done, [&]() { return "Table Headers does not contain key=" + to_hex(block_key); });
ByteView block_header_data{db::from_slice(bh_data.value)};
BlockHeader header;
const auto res{rlp::decode(block_header_data, header)};
ensure(res.has_value(), "Cannot decode block header from rlp=" + to_hex(db::from_slice(bh_data.value)));
ensure(res.has_value(), [&]() { return "Cannot decode block header from rlp=" + to_hex(db::from_slice(bh_data.value)); });

// Read and decode each canonical block body
auto bb_data{block_bodies_table->find(db::to_slice(block_key), /*throw_notfound=*/false)};
Expand Down Expand Up @@ -1383,10 +1383,10 @@ void print_blocks(db::EnvConfig& config, BlockNum from, std::optional<BlockNum>
// Use last block as max block if to is missing and perform range checks
BlockNum last{db::block_number_from_key(last_data.key)};
if (to) {
ensure(from <= *to, "Block from=" + std::to_string(from) + " must not be greater than to=" + std::to_string(*to));
ensure(*to <= last, "Block to=" + std::to_string(*to) + " must not be greater than last=" + std::to_string(last));
ensure(from <= *to, [&]() { return "Block from=" + std::to_string(from) + " must not be greater than to=" + std::to_string(*to); });
ensure(*to <= last, [&]() { return "Block to=" + std::to_string(*to) + " must not be greater than last=" + std::to_string(last); });
} else {
ensure(from <= last, "Block from=" + std::to_string(from) + " must not be greater than last=" + std::to_string(last));
ensure(from <= last, [&]() { return "Block from=" + std::to_string(from) + " must not be greater than last=" + std::to_string(last); });
to = last;
}

Expand All @@ -1396,11 +1396,11 @@ void print_blocks(db::EnvConfig& config, BlockNum from, std::optional<BlockNum>
// Read and decode each block header
auto block_key{db::block_key(block_number)};
auto bh_data{block_headers_table->lower_bound(db::to_slice(block_key), /*throw_notfound=*/false)};
ensure(bh_data.done, "Table Headers does not contain key=" + to_hex(block_key));
ensure(bh_data.done, [&]() { return "Table Headers does not contain key=" + to_hex(block_key); });
ByteView block_header_data{db::from_slice(bh_data.value)};
BlockHeader header;
const auto res{rlp::decode(block_header_data, header)};
ensure(res.has_value(), "Cannot decode block header from rlp=" + to_hex(db::from_slice(bh_data.value)));
ensure(res.has_value(), [&]() { return "Cannot decode block header from rlp=" + to_hex(db::from_slice(bh_data.value)); });

// Read and decode each block body
auto bb_data{block_bodies_table->lower_bound(db::to_slice(block_key), /*throw_notfound=*/false)};
Expand Down
8 changes: 4 additions & 4 deletions cmd/dev/snapshots.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ void open_index(const SnapSettings& settings) {
std::filesystem::path segment_file_path{settings.repository_dir / *settings.snapshot_file_name};
SILK_INFO << "Open index for snapshot: " << segment_file_path;
const auto snapshot_path{snapshots::SnapshotPath::parse(segment_file_path)};
ensure(snapshot_path.has_value(), "open_index: invalid snapshot file " + segment_file_path.filename().string());
ensure(snapshot_path.has_value(), [&]() { return "open_index: invalid snapshot file " + segment_file_path.filename().string(); });
const auto index_path{snapshot_path->index_file()};
std::chrono::time_point start{std::chrono::steady_clock::now()};
rec_split::RecSplitIndex idx{index_path.path()};
Expand Down Expand Up @@ -401,7 +401,7 @@ void lookup_header_by_number(const SnapSettings& settings) {
if (header_snapshot) {
const auto header{header_snapshot->header_by_number(block_number)};
ensure(header.has_value(),
"lookup_header_by_number: " + std::to_string(block_number) + " NOT found in " + header_snapshot->path().filename());
[&]() { return "lookup_header_by_number: " + std::to_string(block_number) + " NOT found in " + header_snapshot->path().filename(); });
SILK_INFO << "Lookup header number: " << block_number << " found in: " << header_snapshot->path().filename();
if (settings.print) {
print_header(*header, header_snapshot->path().filename());
Expand Down Expand Up @@ -438,7 +438,7 @@ void lookup_body_in_one(const SnapSettings& settings, BlockNum block_number, con

std::chrono::time_point start{std::chrono::steady_clock::now()};
const auto body_snapshot{snapshot_repository.get_body_segment(*snapshot_path)};
ensure(body_snapshot, "lookup_body: body segment not found for snapshot file: " + snapshot_path->path().string());
ensure(body_snapshot, [&]() { return "lookup_body: body segment not found for snapshot file: " + snapshot_path->path().string(); });
const auto body{body_snapshot->body_by_number(block_number)};
if (body) {
SILK_INFO << "Lookup body number: " << block_number << " found in: " << body_snapshot->path().filename();
Expand All @@ -461,7 +461,7 @@ void lookup_body_in_all(const SnapSettings& settings, BlockNum block_number) {
if (body_snapshot) {
const auto body{body_snapshot->body_by_number(block_number)};
ensure(body.has_value(),
"lookup_body: " + std::to_string(block_number) + " NOT found in " + body_snapshot->path().filename());
[&]() { return "lookup_body: " + std::to_string(block_number) + " NOT found in " + body_snapshot->path().filename(); });
SILK_INFO << "Lookup body number: " << block_number << " found in: " << body_snapshot->path().filename();
if (settings.print) {
print_body(*body, body_snapshot->path().filename());
Expand Down
38 changes: 30 additions & 8 deletions silkworm/infra/common/ensure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,33 +16,55 @@

#pragma once

#include <functional>
#include <stdexcept>
#include <string>

namespace silkworm {

//! Ensure that condition is met, otherwise raise a logic error with specified message
inline void ensure(bool condition, const std::string& message) {
//! Ensure that condition is met, otherwise raise a logic error with string literal message
template <unsigned int N>
inline void ensure(bool condition, const char (&message)[N]) {
if (!condition) [[unlikely]] {
throw std::logic_error(message);
}
}

//! Ensure that condition is met, otherwise raise a logic error with dynamically built message
//! Usage: `ensure(condition, [&]() { return "Message: " + get_str(); });`
inline void ensure(bool condition, const std::function<std::string()>& messageBuilder) {
if (!condition) [[unlikely]] {
throw std::logic_error(messageBuilder());
}
}

//! Similar to \code ensure with emphasis on invariant violation
inline void ensure_invariant(bool condition, const std::string& message) {
ensure(condition, "Invariant violation: " + message);
template <unsigned int N>
inline void ensure_invariant(bool condition, const char (&message)[N]) {
if (!condition) [[unlikely]] {
throw std::logic_error("Invariant violation: " + std::string{message});
}
}

//! Similar to \code ensure with emphasis on invariant violation
inline void ensure_invariant(bool condition, const std::function<std::string()>& messageBuilder) {
if (!condition) [[unlikely]] {
throw std::logic_error("Invariant violation: " + messageBuilder());
}
}

//! Similar to \code ensure with emphasis on pre-condition violation
inline void ensure_pre_condition(bool condition, const std::string& message) {
inline void ensure_pre_condition(bool condition, const std::function<std::string()>& messageBuilder) {
if (!condition) [[unlikely]] {
throw std::invalid_argument("Pre-condition violation: " + message);
throw std::invalid_argument("Pre-condition violation: " + messageBuilder());
}
}

//! Similar to \code ensure with emphasis on post-condition violation
inline void ensure_post_condition(bool condition, const std::string& message) {
ensure(condition, "Post-condition violation: " + message);
inline void ensure_post_condition(bool condition, const std::function<std::string()>& messageBuilder) {
if (!condition) [[unlikely]] {
throw std::logic_error("Post-condition violation: " + messageBuilder());
}
}

} // namespace silkworm
28 changes: 28 additions & 0 deletions silkworm/infra/common/ensure_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,38 @@ TEST_CASE("ensure") {
CHECK_THROWS_MATCHES(ensure(false, "condition violation"), std::logic_error, Message("condition violation"));
}

TEST_CASE("ensure dynamic message") {
CHECK_NOTHROW(ensure(true, []() { return "ignored"; }));
CHECK_THROWS_AS(ensure(false, []() { return "error"; }), std::logic_error);
CHECK_THROWS_MATCHES(ensure(false, []() { return "condition violation"; }), std::logic_error, Message("condition violation"));
CHECK_THROWS_MATCHES(ensure(false, []() { return "condition violation " + std::to_string(42); }), std::logic_error, Message("condition violation 42"));
}

TEST_CASE("ensure_invariant") {
CHECK_NOTHROW(ensure_invariant(true, "ignored"));
CHECK_THROWS_AS(ensure_invariant(false, "error"), std::logic_error);
CHECK_THROWS_MATCHES(ensure_invariant(false, "x"), std::logic_error, Message("Invariant violation: x"));
}

TEST_CASE("ensure_invariant dynamic message") {
CHECK_NOTHROW(ensure_invariant(true, []() { return "ignored"; }));
CHECK_THROWS_AS(ensure_invariant(false, []() { return "error"; }), std::logic_error);
CHECK_THROWS_MATCHES(ensure_invariant(false, []() { return "x"; }), std::logic_error, Message("Invariant violation: x"));
CHECK_THROWS_MATCHES(ensure_invariant(false, []() { return "x " + std::to_string(42); }), std::logic_error, Message("Invariant violation: x 42"));
}

TEST_CASE("ensure_pre_condition") {
CHECK_NOTHROW(ensure_pre_condition(true, []() { return "ignored"; }));
CHECK_THROWS_AS(ensure_pre_condition(false, []() { return "error"; }), std::logic_error);
CHECK_THROWS_MATCHES(ensure_pre_condition(false, []() { return "x"; }), std::logic_error, Message("Pre-condition violation: x"));
CHECK_THROWS_MATCHES(ensure_pre_condition(false, []() { return "x " + std::to_string(42); }), std::logic_error, Message("Pre-condition violation: x 42"));
}

TEST_CASE("ensure_post_condition") {
CHECK_NOTHROW(ensure_post_condition(true, []() { return "ignored"; }));
CHECK_THROWS_AS(ensure_post_condition(false, []() { return "error"; }), std::logic_error);
CHECK_THROWS_MATCHES(ensure_post_condition(false, []() { return "x"; }), std::logic_error, Message("Post-condition violation: x"));
CHECK_THROWS_MATCHES(ensure_post_condition(false, []() { return "x " + std::to_string(42); }), std::logic_error, Message("Post-condition violation: x 42"));
}

} // namespace silkworm
Loading

0 comments on commit 8859a3f

Please sign in to comment.