From 1a89ab6a1c61e3c84cf1db8c844157d48fcc715e Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Fri, 5 Feb 2021 20:16:39 +0300 Subject: [PATCH 01/86] Batch db requests (#791) * fixup! Signed-off-by: iceseer * format Signed-off-by: iceseer Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- irohad/ametsuchi/impl/postgres_indexer.cpp | 50 +++++++++++++++++----- irohad/ametsuchi/impl/postgres_indexer.hpp | 1 + 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/irohad/ametsuchi/impl/postgres_indexer.cpp b/irohad/ametsuchi/impl/postgres_indexer.cpp index 997065a823d..eff4247b36b 100644 --- a/irohad/ametsuchi/impl/postgres_indexer.cpp +++ b/irohad/ametsuchi/impl/postgres_indexer.cpp @@ -5,6 +5,7 @@ #include "ametsuchi/impl/postgres_indexer.hpp" +#include #include #include #include "cryptography/hash.hpp" @@ -43,12 +44,20 @@ void PostgresIndexer::txPositions( iroha::expected::Result PostgresIndexer::flush() { try { + cache_.clear(); assert(tx_hash_status_.hash.size() == tx_hash_status_.status.size()); if (not tx_hash_status_.hash.empty()) { - sql_ << "INSERT INTO tx_status_by_hash" - "(hash, status) VALUES " - "(:hash, :status);", - soci::use(tx_hash_status_.hash), soci::use(tx_hash_status_.status); + cache_ += + "INSERT INTO tx_status_by_hash" + "(hash, status) VALUES "; + for (size_t ix = 0; ix < tx_hash_status_.hash.size(); ++ix) { + cache_ += fmt::format("('{}','{}')", + tx_hash_status_.hash[ix], + tx_hash_status_.status[ix]); + if (ix != tx_hash_status_.hash.size() - 1) + cache_ += ','; + } + cache_ += ";\n"; tx_hash_status_.hash.clear(); tx_hash_status_.status.clear(); @@ -60,13 +69,30 @@ iroha::expected::Result PostgresIndexer::flush() { assert(tx_positions_.account.size() == tx_positions_.height.size()); assert(tx_positions_.account.size() == tx_positions_.index.size()); if (!tx_positions_.account.empty()) { - sql_ << "INSERT INTO tx_positions" - "(creator_id, hash, asset_id, ts, height, index) VALUES " - "(:creator_id, :hash, :asset_id, :ts, :height, :index) ON " - "CONFLICT DO NOTHING;", - soci::use(tx_positions_.account), soci::use(tx_positions_.hash), - soci::use(tx_positions_.asset_id), soci::use(tx_positions_.ts), - soci::use(tx_positions_.height), soci::use(tx_positions_.index); + cache_ += + "INSERT INTO tx_positions" + "(creator_id, hash, asset_id, ts, height, index) VALUES "; + for (size_t ix = 0; ix < tx_positions_.account.size(); ++ix) { + if (tx_positions_.asset_id[ix]) { + cache_ += fmt::format("('{}','{}','{}',{},{},{})", + tx_positions_.account[ix], + tx_positions_.hash[ix], + *tx_positions_.asset_id[ix], + tx_positions_.ts[ix], + tx_positions_.height[ix], + tx_positions_.index[ix]); + } else { + cache_ += fmt::format("('{}','{}',NULL,{},{},{})", + tx_positions_.account[ix], + tx_positions_.hash[ix], + tx_positions_.ts[ix], + tx_positions_.height[ix], + tx_positions_.index[ix]); + } + if (ix != tx_positions_.account.size() - 1) + cache_ += ','; + } + cache_ += " ON CONFLICT DO NOTHING;\n"; tx_positions_.account.clear(); tx_positions_.hash.clear(); @@ -76,6 +102,8 @@ iroha::expected::Result PostgresIndexer::flush() { tx_positions_.index.clear(); } + if (!cache_.empty()) + sql_ << cache_; } catch (const std::exception &e) { return e.what(); } diff --git a/irohad/ametsuchi/impl/postgres_indexer.hpp b/irohad/ametsuchi/impl/postgres_indexer.hpp index c38ba01a473..323398804c8 100644 --- a/irohad/ametsuchi/impl/postgres_indexer.hpp +++ b/irohad/ametsuchi/impl/postgres_indexer.hpp @@ -60,6 +60,7 @@ namespace iroha { bool is_committed); soci::session &sql_; + std::string cache_; }; } // namespace ametsuchi From dc65ec199aaebf0ebacad7d4d8a520129ffbfeed Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Mon, 8 Feb 2021 10:07:51 +0300 Subject: [PATCH 02/86] fixup! no clone (#796) Signed-off-by: iceseer Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- shared_model/backend/protobuf/common_objects/signature.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared_model/backend/protobuf/common_objects/signature.hpp b/shared_model/backend/protobuf/common_objects/signature.hpp index b750418f7f1..c3bcbecf101 100644 --- a/shared_model/backend/protobuf/common_objects/signature.hpp +++ b/shared_model/backend/protobuf/common_objects/signature.hpp @@ -33,7 +33,7 @@ namespace shared_model { private: interface::Signature *clone() const override { - return new Signature(proto_); + return new Signature(iroha::protocol::Signature(*proto_)); } }; } // namespace proto From 4fd18fae3ec3385a5981e7aaaed09e7841433e15 Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Fri, 12 Feb 2021 11:11:13 +0100 Subject: [PATCH 03/86] Yac: do not consider next round as Future (#742) * YacGate: ignore Future messages for the next block Signed-off-by: Andrei Lebedev * YacGate: initialize with ledger state Signed-off-by: Andrei Lebedev Co-authored-by: kamilsa --- irohad/consensus/yac/impl/yac_gate_impl.cpp | 12 ++++++++++++ irohad/consensus/yac/impl/yac_gate_impl.hpp | 1 + irohad/main/application.cpp | 14 +++++++++++--- irohad/main/impl/consensus_init.cpp | 2 ++ irohad/main/impl/consensus_init.hpp | 1 + test/module/irohad/consensus/yac/yac_gate_test.cpp | 13 +++++++------ 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/irohad/consensus/yac/impl/yac_gate_impl.cpp b/irohad/consensus/yac/impl/yac_gate_impl.cpp index 12a556aa0ac..3a3ffa72943 100644 --- a/irohad/consensus/yac/impl/yac_gate_impl.cpp +++ b/irohad/consensus/yac/impl/yac_gate_impl.cpp @@ -39,6 +39,7 @@ namespace iroha { std::shared_ptr hash_gate, std::shared_ptr orderer, boost::optional alternative_order, + std::shared_ptr ledger_state, std::shared_ptr hash_provider, std::shared_ptr block_creator, std::shared_ptr @@ -49,6 +50,7 @@ namespace iroha { : log_(std::move(log)), current_hash_(), alternative_order_(std::move(alternative_order)), + current_ledger_state_(std::move(ledger_state)), published_events_([&] { rxcpp::observable outcomes = hash_gate->onOutcome(); rxcpp::observable delayed_outcomes = outcomes.concat_map( @@ -258,6 +260,16 @@ namespace iroha { return rxcpp::observable<>::empty(); } + if (current_ledger_state_->top_block_info.height + 1 + >= hash.vote_round.block_round) { + log_->info( + "Difference between top height {} and future block round {} is " + "less than 2, skipped", + current_ledger_state_->top_block_info.height, + hash.vote_round.block_round); + return rxcpp::observable<>::empty(); + } + assert(hash.vote_round.block_round > current_hash_.vote_round.block_round); diff --git a/irohad/consensus/yac/impl/yac_gate_impl.hpp b/irohad/consensus/yac/impl/yac_gate_impl.hpp index 3f6e7635e82..ddc171311b1 100644 --- a/irohad/consensus/yac/impl/yac_gate_impl.hpp +++ b/irohad/consensus/yac/impl/yac_gate_impl.hpp @@ -39,6 +39,7 @@ namespace iroha { std::shared_ptr hash_gate, std::shared_ptr orderer, boost::optional alternative_order, + std::shared_ptr ledger_state, std::shared_ptr hash_provider, std::shared_ptr block_creator, std::shared_ptr diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index b35a7bd4120..20e3fdb8f75 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -706,10 +706,16 @@ Irohad::RunResult Irohad::initConsensusGate() { auto &block = boost::get>(&block_var)->value; + auto initial_ledger_state = storage->getLedgerState(); + if (not initial_ledger_state) { + return expected::makeError("Failed to fetch ledger state!"); + } + consensus_gate = yac_init->initConsensusGate( {block->height(), ordering::kFirstRejectRound}, storage, opt_alternative_peers_, + *initial_ledger_state, simulator, block_loader, keypair, @@ -1004,8 +1010,10 @@ Irohad::RunResult Irohad::run() { return expected::makeError("Failed to fetch ledger peers!"); } - auto initial_ledger_state = std::make_shared( - std::move(peers.value()), block->height(), block->hash()); + auto initial_ledger_state = storage->getLedgerState(); + if (not initial_ledger_state) { + return expected::makeError("Failed to fetch ledger state!"); + } pcs->onSynchronization().subscribe( ordering_init.sync_event_notifier.get_subscriber()); @@ -1018,7 +1026,7 @@ Irohad::RunResult Irohad::run() { synchronizer::SynchronizationEvent{ SynchronizationOutcomeType::kCommit, {block_height, ordering::kFirstRejectRound}, - initial_ledger_state}); + *initial_ledger_state}); return {}; }; } diff --git a/irohad/main/impl/consensus_init.cpp b/irohad/main/impl/consensus_init.cpp index 65708ae608c..6f8ea534335 100644 --- a/irohad/main/impl/consensus_init.cpp +++ b/irohad/main/impl/consensus_init.cpp @@ -89,6 +89,7 @@ namespace iroha { peer_query_factory, boost::optional alternative_peers, + std::shared_ptr ledger_state, std::shared_ptr block_creator, std::shared_ptr block_loader, const shared_model::crypto::Keypair &keypair, @@ -133,6 +134,7 @@ namespace iroha { std::move(peer_orderer), alternative_peers | [](auto &peers) { return ClusterOrdering::create(peers); }, + std::move(ledger_state), hash_provider, block_creator, std::move(consensus_result_cache), diff --git a/irohad/main/impl/consensus_init.hpp b/irohad/main/impl/consensus_init.hpp index f8000aea375..599c6e07e42 100644 --- a/irohad/main/impl/consensus_init.hpp +++ b/irohad/main/impl/consensus_init.hpp @@ -39,6 +39,7 @@ namespace iroha { std::shared_ptr peer_query_factory, boost::optional alternative_peers, + std::shared_ptr ledger_state, std::shared_ptr block_creator, std::shared_ptr block_loader, const shared_model::crypto::Keypair &keypair, diff --git a/test/module/irohad/consensus/yac/yac_gate_test.cpp b/test/module/irohad/consensus/yac/yac_gate_test.cpp index 0c28070bf1d..8805bd832b8 100644 --- a/test/module/irohad/consensus/yac/yac_gate_test.cpp +++ b/test/module/irohad/consensus/yac/yac_gate_test.cpp @@ -105,19 +105,20 @@ class YacGateTest : public ::testing::Test { ON_CALL(*block_creator, onBlock()) .WillByDefault(Return(block_notifier.get_observable())); + auto peer = makePeer("127.0.0.1", "111"_hex_pubkey); + ledger_state = std::make_shared( + shared_model::interface::types::PeerList{std::move(peer)}, + block->height() - 1, + block->prevHash()); + gate = std::make_shared(std::move(hash_gate_ptr), std::move(peer_orderer_ptr), alternative_order, + ledger_state, hash_provider, block_creator, block_cache, getTestLogger("YacGateImpl")); - - auto peer = makePeer("127.0.0.1", "111"_hex_pubkey); - ledger_state = std::make_shared( - shared_model::interface::types::PeerList{std::move(peer)}, - block->height() - 1, - block->prevHash()); } iroha::consensus::Round round{2, 1}; From f0e1e7b5578157d2f57dd0c429a3b993f34fef72 Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 25 Feb 2021 15:59:24 +0300 Subject: [PATCH 04/86] add wsv restore mode - sync blockstore (#812) * add wsv restore mode - sync blockstore add --drop_state param, param --reuse_state is default Signed-off-by: Alexey-N-Chernyshov * reload blockstore atomic file creation restore blockstore file content validation Signed-off-by: Alexey-N-Chernyshov * fix integration tests with new param Signed-off-by: Alexey-N-Chernyshov Signed-off-by: Alexey --- docs/source/maintenance/restarting_node.rst | 18 +- irohad/ametsuchi/block_query.hpp | 5 + irohad/ametsuchi/block_storage.hpp | 7 +- irohad/ametsuchi/impl/flat_file/flat_file.cpp | 69 ++++--- irohad/ametsuchi/impl/flat_file/flat_file.hpp | 15 +- .../impl/flat_file_block_storage.cpp | 6 +- .../impl/flat_file_block_storage.hpp | 2 + .../impl/in_memory_block_storage.cpp | 3 + .../impl/in_memory_block_storage.hpp | 2 + .../ametsuchi/impl/postgres_block_query.cpp | 4 + .../ametsuchi/impl/postgres_block_query.hpp | 6 +- .../ametsuchi/impl/postgres_block_storage.cpp | 4 + .../ametsuchi/impl/postgres_block_storage.hpp | 2 + irohad/ametsuchi/impl/wsv_restorer_impl.cpp | 182 ++++++++++------- irohad/ametsuchi/impl/wsv_restorer_impl.hpp | 13 +- irohad/ametsuchi/key_value_storage.hpp | 5 + irohad/ametsuchi/wsv_restorer.hpp | 5 +- irohad/main/application.cpp | 185 +++++++++--------- irohad/main/application.hpp | 51 +---- irohad/main/irohad.cpp | 79 ++++---- irohad/main/startup_params.hpp | 15 +- .../integration_test_framework.cpp | 53 +++-- .../integration_test_framework.hpp | 5 +- .../integration_framework/iroha_instance.cpp | 46 +---- .../integration_framework/iroha_instance.hpp | 32 +-- .../integration_framework/test_irohad.hpp | 29 +-- .../irohad/ametsuchi/ametsuchi_test.cpp | 45 +++-- .../irohad/ametsuchi/mock_block_query.hpp | 1 + .../irohad/ametsuchi/mock_block_storage.hpp | 9 +- test/system/irohad_test.cpp | 29 +-- 30 files changed, 488 insertions(+), 439 deletions(-) diff --git a/docs/source/maintenance/restarting_node.rst b/docs/source/maintenance/restarting_node.rst index 61eca810e0b..7adc7fd07e5 100644 --- a/docs/source/maintenance/restarting_node.rst +++ b/docs/source/maintenance/restarting_node.rst @@ -38,18 +38,11 @@ Although it can be a great idea for some of the cases, but please consider that Please, restore it from blocks. -Enabling WSV Reuse +Dropping WSV ^^^^^^^^^^^^^^^^^^ -If you want to reuse WSV state, start Iroha with `--reuse_state` flag. -Given this flag, Iroha will not reset or overwrite the state database if it fails to start for whatever reason. - - -Enabling WSV Reuse -^^^^^^^^^^^^^^^^^^ - -If you want to reuse WSV state, start Iroha with `--reuse_state` flag. -Given this flag, Iroha will not reset or overwrite the state database if it fails to start for whatever reason. +By default Iroha reuses WSV state on startup, so there is no need in `--reuse_state` flag anymore. However, it is left for backward compatibility. +If you want to drop WSV state, start Iroha with '--drop_state' flag. Given this flag, Iroha will reset and overwrite the state database. State Database Schema version ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -79,3 +72,8 @@ Then it will find all migration paths that will transition your database to the .. seealso:: `Here `_ are some details about different migration cases and examples you can check out to perform migration + +Synchronize WSV mode. +^^^^^^^^^^^^^^^^^^^^^ + +Specify '--wait_for_new_blocks' options for WSV synchronization mode. Iroha restores WSV from blockstore and waits for new blocks to be added externally. In this mode Iroha will not perform network operations. diff --git a/irohad/ametsuchi/block_query.hpp b/irohad/ametsuchi/block_query.hpp index 2c28fb1f9c2..a256abe1eff 100644 --- a/irohad/ametsuchi/block_query.hpp +++ b/irohad/ametsuchi/block_query.hpp @@ -50,6 +50,11 @@ namespace iroha { virtual shared_model::interface::types::HeightType getTopBlockHeight() = 0; + /** + * Reloads blockstore + */ + virtual void reloadBlockstore() = 0; + /** * Synchronously checks whether transaction with given hash is present in * any block diff --git a/irohad/ametsuchi/block_storage.hpp b/irohad/ametsuchi/block_storage.hpp index 0ce747b7393..53eb2ac8184 100644 --- a/irohad/ametsuchi/block_storage.hpp +++ b/irohad/ametsuchi/block_storage.hpp @@ -6,11 +6,11 @@ #ifndef IROHA_BLOCK_STORAGE_HPP #define IROHA_BLOCK_STORAGE_HPP +#include #include #include #include -#include #include "common/result_fwd.hpp" #include "interfaces/iroha_internal/block.hpp" @@ -41,6 +41,11 @@ namespace iroha { */ virtual size_t size() const = 0; + /** + * Reloads blocks in case their were modified externally + */ + virtual void reload() = 0; + /** * Clears the contents of storage */ diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.cpp b/irohad/ametsuchi/impl/flat_file/flat_file.cpp index 17f5966ea8c..b4499175edf 100644 --- a/irohad/ametsuchi/impl/flat_file/flat_file.cpp +++ b/irohad/ametsuchi/impl/flat_file/flat_file.cpp @@ -5,29 +5,32 @@ #include "ametsuchi/impl/flat_file/flat_file.hpp" -#include -#include -#include -#include - #include #include #include #include #include +#include +#include +#include +#include + #include "common/files.hpp" #include "common/result.hpp" #include "logger/logger.hpp" #ifdef _WIN32 -#include #include +#include #endif using namespace iroha::ametsuchi; using Identifier = FlatFile::Identifier; using BlockIdCollectionType = FlatFile::BlockIdCollectionType; +const std::string FlatFile::kTempFileExtension = ".tmp"; +const std::regex FlatFile::kBlockFilenameRegex = std::regex("[0-9]{16}"); + // ----------| public API |---------- std::string FlatFile::id_to_name(Identifier id) { @@ -57,28 +60,19 @@ FlatFile::create(const std::string &path, logger::LoggerPtr log) { "Cannot create storage dir '{}': {}", path, err.message()); } - BlockIdCollectionType files_found; - for (auto it = boost::filesystem::directory_iterator{path}; - it != boost::filesystem::directory_iterator{}; - ++it) { - if (auto id = FlatFile::name_to_id(it->path().filename().string())) { - files_found.insert(*id); - } else { - boost::filesystem::remove(it->path()); - } - } - - return std::make_unique( - path, std::move(files_found), private_tag{}, std::move(log)); + return std::make_unique(path, private_tag{}, std::move(log)); } bool FlatFile::add(Identifier id, const Bytes &block) { // TODO(x3medima17): Change bool to generic Result return type + const auto tmp_file_name = boost::filesystem::path{dump_dir_} + / (id_to_name(id) + kTempFileExtension); const auto file_name = boost::filesystem::path{dump_dir_} / id_to_name(id); // Write block to binary file - if (boost::filesystem::exists(file_name)) { + if (boost::filesystem::exists(tmp_file_name) + || boost::filesystem::exists(file_name)) { // File already exist log_->warn("insertion for {} failed, because file already exists", id); return false; @@ -86,7 +80,7 @@ bool FlatFile::add(Identifier id, const Bytes &block) { // New file will be created boost::iostreams::stream file; try { - file.open(file_name, std::ofstream::binary); + file.open(tmp_file_name, std::ofstream::binary); } catch (std::ios_base::failure const &e) { log_->warn("Cannot open file by index {} for writing: {}", id, e.what()); return false; @@ -119,6 +113,14 @@ bool FlatFile::add(Identifier id, const Bytes &block) { return false; } + boost::system::error_code error_code; + boost::filesystem::rename(tmp_file_name, file_name, error_code); + if (error_code != boost::system::errc::success) { + log_->error( + "insertion for {} failed, because {}", id, error_code.message()); + return false; + } + available_blocks_.insert(id); return true; } @@ -142,6 +144,24 @@ Identifier FlatFile::last_id() const { return (available_blocks_.empty()) ? 0 : *available_blocks_.rbegin(); } +void FlatFile::reload() { + available_blocks_.clear(); + for (auto it = boost::filesystem::directory_iterator{dump_dir_}; + it != boost::filesystem::directory_iterator{}; + ++it) { + // skip non-block files + if (!std::regex_match(it->path().filename().string(), + kBlockFilenameRegex)) { + continue; + } + if (auto id = FlatFile::name_to_id(it->path().filename().string())) { + available_blocks_.insert(*id); + } else { + boost::filesystem::remove(it->path()); + } + } +} + void FlatFile::dropAll() { iroha::remove_dir_contents(dump_dir_, log_); available_blocks_.clear(); @@ -154,9 +174,8 @@ const BlockIdCollectionType &FlatFile::blockIdentifiers() const { // ----------| private API |---------- FlatFile::FlatFile(std::string path, - BlockIdCollectionType existing_files, FlatFile::private_tag, logger::LoggerPtr log) - : dump_dir_(std::move(path)), - available_blocks_(std::move(existing_files)), - log_{std::move(log)} {} + : dump_dir_(std::move(path)), log_{std::move(log)} { + reload(); +} diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.hpp b/irohad/ametsuchi/impl/flat_file/flat_file.hpp index 46f35474c21..726317087db 100644 --- a/irohad/ametsuchi/impl/flat_file/flat_file.hpp +++ b/irohad/ametsuchi/impl/flat_file/flat_file.hpp @@ -6,11 +6,11 @@ #ifndef IROHA_FLAT_FILE_HPP #define IROHA_FLAT_FILE_HPP -#include "ametsuchi/key_value_storage.hpp" - #include +#include #include +#include "ametsuchi/key_value_storage.hpp" #include "common/result_fwd.hpp" #include "logger/logger_fwd.hpp" @@ -34,6 +34,10 @@ namespace iroha { static const uint32_t DIGIT_CAPACITY = 16; + static const std::string kTempFileExtension; + + static const std::regex kBlockFilenameRegex; + /** * Convert id to a string representation. The string representation is * always DIGIT_CAPACITY-character width regardless of the value of `id`. @@ -72,6 +76,8 @@ namespace iroha { Identifier last_id() const override; + void reload() override; + void dropAll() override; /** @@ -94,11 +100,9 @@ namespace iroha { /** * Create storage in path * @param path - folder of storage - * @param existing_files - collection of existing files names * @param log to print progress */ FlatFile(std::string path, - BlockIdCollectionType existing_files, FlatFile::private_tag, logger::LoggerPtr log); @@ -108,6 +112,9 @@ namespace iroha { */ const std::string dump_dir_; + /** + * Blocks in storage, can be modified externally + */ BlockIdCollectionType available_blocks_; logger::LoggerPtr log_; diff --git a/irohad/ametsuchi/impl/flat_file_block_storage.cpp b/irohad/ametsuchi/impl/flat_file_block_storage.cpp index fc63b50e0e9..54c593f28f7 100644 --- a/irohad/ametsuchi/impl/flat_file_block_storage.cpp +++ b/irohad/ametsuchi/impl/flat_file_block_storage.cpp @@ -58,7 +58,11 @@ FlatFileBlockStorage::fetch( } size_t FlatFileBlockStorage::size() const { - return flat_file_storage_->blockIdentifiers().size(); + return flat_file_storage_->last_id(); +} + +void FlatFileBlockStorage::reload() { + flat_file_storage_->reload(); } void FlatFileBlockStorage::clear() { diff --git a/irohad/ametsuchi/impl/flat_file_block_storage.hpp b/irohad/ametsuchi/impl/flat_file_block_storage.hpp index 968b28d6af2..3f71bf04cc7 100644 --- a/irohad/ametsuchi/impl/flat_file_block_storage.hpp +++ b/irohad/ametsuchi/impl/flat_file_block_storage.hpp @@ -30,6 +30,8 @@ namespace iroha { size_t size() const override; + void reload() override; + void clear() override; expected::Result forEach( diff --git a/irohad/ametsuchi/impl/in_memory_block_storage.cpp b/irohad/ametsuchi/impl/in_memory_block_storage.cpp index e6bfc9fbc0e..126c054e239 100644 --- a/irohad/ametsuchi/impl/in_memory_block_storage.cpp +++ b/irohad/ametsuchi/impl/in_memory_block_storage.cpp @@ -27,6 +27,9 @@ InMemoryBlockStorage::fetch( size_t InMemoryBlockStorage::size() const { return block_store_.size(); } +void InMemoryBlockStorage::reload() { + // no need to reload +} void InMemoryBlockStorage::clear() { block_store_.clear(); diff --git a/irohad/ametsuchi/impl/in_memory_block_storage.hpp b/irohad/ametsuchi/impl/in_memory_block_storage.hpp index 1127a859bf4..4f0011b874b 100644 --- a/irohad/ametsuchi/impl/in_memory_block_storage.hpp +++ b/irohad/ametsuchi/impl/in_memory_block_storage.hpp @@ -26,6 +26,8 @@ namespace iroha { size_t size() const override; + void reload() override; + void clear() override; expected::Result forEach( diff --git a/irohad/ametsuchi/impl/postgres_block_query.cpp b/irohad/ametsuchi/impl/postgres_block_query.cpp index cc27326dc3c..dbc735c4a66 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.cpp +++ b/irohad/ametsuchi/impl/postgres_block_query.cpp @@ -44,6 +44,10 @@ namespace iroha { return block_storage_.size(); } + void PostgresBlockQuery::reloadBlockstore() { + block_storage_.reload(); + } + std::optional PostgresBlockQuery::checkTxPresence( const shared_model::crypto::Hash &hash) { int res = -1; diff --git a/irohad/ametsuchi/impl/postgres_block_query.hpp b/irohad/ametsuchi/impl/postgres_block_query.hpp index a2e0c31359a..e2f4d409222 100644 --- a/irohad/ametsuchi/impl/postgres_block_query.hpp +++ b/irohad/ametsuchi/impl/postgres_block_query.hpp @@ -6,9 +6,9 @@ #ifndef IROHA_POSTGRES_BLOCK_QUERY_HPP #define IROHA_POSTGRES_BLOCK_QUERY_HPP -#include "ametsuchi/block_query.hpp" - #include + +#include "ametsuchi/block_query.hpp" #include "ametsuchi/block_storage.hpp" #include "logger/logger_fwd.hpp" @@ -33,6 +33,8 @@ namespace iroha { shared_model::interface::types::HeightType getTopBlockHeight() override; + void reloadBlockstore() override; + std::optional checkTxPresence( const shared_model::crypto::Hash &hash) override; diff --git a/irohad/ametsuchi/impl/postgres_block_storage.cpp b/irohad/ametsuchi/impl/postgres_block_storage.cpp index 4c1f89cbc76..ca03f12eb87 100644 --- a/irohad/ametsuchi/impl/postgres_block_storage.cpp +++ b/irohad/ametsuchi/impl/postgres_block_storage.cpp @@ -146,6 +146,10 @@ size_t PostgresBlockStorage::size() const { .value_or(0); } +void PostgresBlockStorage::reload() { + // no need to reload +} + void PostgresBlockStorage::clear() { soci::session sql(*pool_wrapper_->connection_pool_); soci::statement st = (sql.prepare << "TRUNCATE " << table_name_); diff --git a/irohad/ametsuchi/impl/postgres_block_storage.hpp b/irohad/ametsuchi/impl/postgres_block_storage.hpp index 77005cfe979..2bef03d382c 100644 --- a/irohad/ametsuchi/impl/postgres_block_storage.hpp +++ b/irohad/ametsuchi/impl/postgres_block_storage.hpp @@ -41,6 +41,8 @@ namespace iroha { size_t size() const override; + void reload() override; + void clear() override; expected::Result forEach( diff --git a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp index 36627ab8f93..5ed2065ec61 100644 --- a/irohad/ametsuchi/impl/wsv_restorer_impl.cpp +++ b/irohad/ametsuchi/impl/wsv_restorer_impl.cpp @@ -5,7 +5,9 @@ #include "wsv_restorer_impl.hpp" +#include #include + #include "ametsuchi/block_query.hpp" #include "ametsuchi/block_storage.hpp" #include "ametsuchi/block_storage_factory.hpp" @@ -23,6 +25,14 @@ using shared_model::interface::types::HeightType; namespace { + using namespace std::chrono_literals; + + /** + * Time to wait for new block in blockstore for wait-for-new-blocks restore + * mode + */ + static constexpr std::chrono::milliseconds kWaitForBlockTime = 5000ms; + /** * Stub implementation used to restore WSV. Check the method descriptions for * details @@ -51,6 +61,8 @@ namespace { return 0; } + void reload() override {} + void clear() override {} /** @@ -156,83 +168,121 @@ namespace iroha::ametsuchi { shared_model::interface::Block>> interface_validator, std::unique_ptr> proto_validator, - std::shared_ptr validator) + std::shared_ptr validator, + logger::LoggerPtr log) : interface_validator_{std::move(interface_validator)}, proto_validator_{std::move(proto_validator)}, - validator_{std::move(validator)} {} + validator_{std::move(validator)}, + log_{std::move(log)} {} - CommitResult WsvRestorerImpl::restoreWsv(Storage &storage) { + CommitResult WsvRestorerImpl::restoreWsv(Storage &storage, + bool wait_for_new_blocks) { return storage.createCommandExecutor() | - [this, &storage](auto &&command_executor) -> CommitResult { + [this, &storage, wait_for_new_blocks]( + std::shared_ptr command_executor) + -> CommitResult { BlockStorageStubFactory storage_factory; - return storage.createMutableStorage(std::move(command_executor), - storage_factory) - | [this, &storage](auto &&mutable_storage) -> CommitResult { - auto block_query = storage.getBlockQuery(); - if (not block_query) { - return expected::makeError("Cannot create BlockQuery"); - } + CommitResult res; + auto block_query = storage.getBlockQuery(); + auto last_block_in_storage = block_query->getTopBlockHeight(); - const auto last_block_in_storage = block_query->getTopBlockHeight(); - const auto wsv_ledger_state = storage.getLedgerState(); - - shared_model::interface::types::HeightType wsv_ledger_height; - if (wsv_ledger_state) { - const auto &wsv_top_block_info = - wsv_ledger_state.value()->top_block_info; - wsv_ledger_height = wsv_top_block_info.height; - if (wsv_ledger_height > last_block_in_storage) { - return fmt::format( - "WSV state (height {}) is more recent " - "than block storage (height {}).", - wsv_ledger_height, - last_block_in_storage); + do { + res = storage.createMutableStorage(command_executor, storage_factory) | + [this, + &storage, + &block_query, + &last_block_in_storage, + wait_for_new_blocks](auto &&mutable_storage) -> CommitResult { + if (not block_query) { + return expected::makeError("Cannot create BlockQuery"); } - // check that a block with that height is present in the block - // storage and that its hash matches - auto check_top_block = - block_query->getBlock(wsv_top_block_info.height) - .match( - [&wsv_top_block_info]( - const auto &block_from_block_storage) - -> expected::Result { - if (block_from_block_storage.value->hash() - != wsv_top_block_info.top_hash) { - return fmt::format( - "The hash of block applied to WSV ({}) " - "does not match the hash of the block " - "from block storage ({}).", - wsv_top_block_info.top_hash, - block_from_block_storage.value->hash()); - } - return expected::Value{}; - }, - [](expected::Error &&error) - -> expected::Result { - return std::move(error).error.message; - }); - if (auto e = expected::resultToOptionalError(check_top_block)) { - return fmt::format( - "WSV top block (height {}) check failed: {} " - "Please check that WSV matches block storage " - "or avoid reusing WSV.", - wsv_ledger_height, - e.value()); + + const auto wsv_ledger_state = storage.getLedgerState(); + + shared_model::interface::types::HeightType wsv_ledger_height; + if (wsv_ledger_state) { + const auto &wsv_top_block_info = + wsv_ledger_state.value()->top_block_info; + wsv_ledger_height = wsv_top_block_info.height; + if (wsv_ledger_height > last_block_in_storage) { + return fmt::format( + "WSV state (height {}) is more recent " + "than block storage (height {}).", + wsv_ledger_height, + last_block_in_storage); + } + // check that a block with that height is present in the block + // storage and that its hash matches + auto check_top_block = + block_query->getBlock(wsv_top_block_info.height) + .match( + [&wsv_top_block_info]( + const auto &block_from_block_storage) + -> expected::Result { + if (block_from_block_storage.value->hash() + != wsv_top_block_info.top_hash) { + return fmt::format( + "The hash of block applied to WSV ({}) " + "does not match the hash of the block " + "from block storage ({}).", + wsv_top_block_info.top_hash, + block_from_block_storage.value->hash()); + } + return expected::Value{}; + }, + [](expected::Error &&error) + -> expected::Result { + return std::move(error).error.message; + }); + if (auto e = expected::resultToOptionalError(check_top_block)) { + return fmt::format( + "WSV top block (height {}) check failed: {} " + "Please check that WSV matches block storage " + "or avoid reusing WSV.", + wsv_ledger_height, + e.value()); + } + } else { + wsv_ledger_height = 0; } - } else { - wsv_ledger_height = 0; + + return reindexBlocks(storage, + mutable_storage, + *block_query, + *interface_validator_, + *proto_validator_, + *validator_, + wsv_ledger_height + 1, + last_block_in_storage); + }; + if (hasError(res)) { + break; } - return reindexBlocks(storage, - mutable_storage, - *block_query, - *interface_validator_, - *proto_validator_, - *validator_, - wsv_ledger_height + 1, - last_block_in_storage); - }; + while (wait_for_new_blocks) { + std::this_thread::sleep_for(kWaitForBlockTime); + block_query->reloadBlockstore(); + auto new_last_block = block_query->getTopBlockHeight(); + + // try to load block to ensure it is written completely + auto block_result = block_query->getBlock(new_last_block); + while (hasError(block_result) + && (new_last_block > last_block_in_storage)) { + --new_last_block; + auto block_result = block_query->getBlock(new_last_block); + }; + + if (new_last_block > last_block_in_storage) { + last_block_in_storage = new_last_block; + log_->info("Blockstore has new blocks from {} to {}, restore them.", + last_block_in_storage, + new_last_block); + break; + } + } + } while (wait_for_new_blocks); + return res; }; } } // namespace iroha::ametsuchi diff --git a/irohad/ametsuchi/impl/wsv_restorer_impl.hpp b/irohad/ametsuchi/impl/wsv_restorer_impl.hpp index db8201c63a6..6acbc8b2fde 100644 --- a/irohad/ametsuchi/impl/wsv_restorer_impl.hpp +++ b/irohad/ametsuchi/impl/wsv_restorer_impl.hpp @@ -6,10 +6,10 @@ #ifndef IROHA_WSVRESTORERIMPL_HPP #define IROHA_WSVRESTORERIMPL_HPP -#include "ametsuchi/wsv_restorer.hpp" - #include "ametsuchi/ledger_state.hpp" +#include "ametsuchi/wsv_restorer.hpp" #include "common/result.hpp" +#include "logger/logger_fwd.hpp" namespace shared_model { namespace interface { @@ -41,17 +41,21 @@ namespace iroha { shared_model::interface::Block>> interface_validator, std::unique_ptr> proto_validator, - std::shared_ptr validator); + std::shared_ptr validator, + logger::LoggerPtr log); virtual ~WsvRestorerImpl() = default; /** * Recover WSV (World State View). * Drop storage and apply blocks one by one. * @param storage of blocks in ledger + * @param wait_for_new_blocks - flag for wait for new blocks mode. + * Method waits for new blocks in block storage. * @return ledger state after restoration on success, otherwise error * string */ - CommitResult restoreWsv(Storage &storage) override; + CommitResult restoreWsv(Storage &storagem, + bool wait_for_new_blocks) override; private: std::unique_ptr> proto_validator_; std::shared_ptr validator_; + logger::LoggerPtr log_; }; } // namespace ametsuchi diff --git a/irohad/ametsuchi/key_value_storage.hpp b/irohad/ametsuchi/key_value_storage.hpp index 91d09f6a4b6..8beacb2d03c 100644 --- a/irohad/ametsuchi/key_value_storage.hpp +++ b/irohad/ametsuchi/key_value_storage.hpp @@ -49,6 +49,11 @@ namespace iroha { */ virtual Identifier last_id() const = 0; + /** + * Reloads data in case it was modified externally + */ + virtual void reload() = 0; + virtual void dropAll() = 0; virtual ~KeyValueStorage() = default; diff --git a/irohad/ametsuchi/wsv_restorer.hpp b/irohad/ametsuchi/wsv_restorer.hpp index 5bc2e93c59f..d643053fb34 100644 --- a/irohad/ametsuchi/wsv_restorer.hpp +++ b/irohad/ametsuchi/wsv_restorer.hpp @@ -22,10 +22,13 @@ namespace iroha { /** * Recover WSV (World State View). * @param storage storage of blocks in ledger + * @param wait_for_new_blocks - flag for wait for new blocks mode. + * Method waits for new blocks in block storage. * @return ledger state after restoration on success, otherwise error * string */ - virtual CommitResult restoreWsv(Storage &storage) = 0; + virtual CommitResult restoreWsv(Storage &storage, + bool wait_for_new_blocks) = 0; }; } // namespace ametsuchi diff --git a/irohad/main/application.cpp b/irohad/main/application.cpp index 20e3fdb8f75..02be9fbeb19 100644 --- a/irohad/main/application.cpp +++ b/irohad/main/application.cpp @@ -5,10 +5,10 @@ #include "main/application.hpp" -#include - #include +#include #include + #include "ametsuchi/impl/pool_wrapper.hpp" #include "ametsuchi/impl/storage_impl.hpp" #include "ametsuchi/impl/tx_presence_cache_impl.hpp" @@ -89,58 +89,40 @@ using namespace iroha::synchronizer; using namespace iroha::torii; using namespace iroha::consensus::yac; -using namespace std::chrono_literals; - using shared_model::interface::types::PublicKeyHexStringView; /// Consensus consistency model type. static constexpr iroha::consensus::yac::ConsistencyModel kConsensusConsistencyModel = iroha::consensus::yac::ConsistencyModel::kCft; +static constexpr uint32_t kStaleStreamMaxRoundsDefault = 2; +static constexpr uint32_t kMstExpirationTimeDefault = 1440; +static constexpr uint32_t kMaxRoundsDelayDefault = 3000; + /** * Configuring iroha daemon */ Irohad::Irohad( - const boost::optional &block_store_dir, + const IrohadConfig &config, std::unique_ptr pg_opt, const std::string &listen_ip, - size_t torii_port, - size_t internal_port, - size_t max_proposal_size, - std::chrono::milliseconds proposal_delay, - std::chrono::milliseconds vote_delay, - std::chrono::minutes mst_expiration_time, - const shared_model::crypto::Keypair &keypair, - std::chrono::milliseconds max_rounds_delay, - size_t stale_stream_max_rounds, - boost::optional - opt_alternative_peers, + const boost::optional &keypair, logger::LoggerManagerTreePtr logger_manager, StartupWsvDataPolicy startup_wsv_data_policy, + StartupWsvSynchronizationPolicy startup_wsv_sync_policy, std::shared_ptr grpc_channel_params, const boost::optional &opt_mst_gossip_params, - const boost::optional &torii_tls_params, boost::optional inter_peer_tls_config) - : block_store_dir_(block_store_dir), + : config_(config), listen_ip_(listen_ip), - torii_port_(torii_port), - torii_tls_params_(torii_tls_params), - internal_port_(internal_port), - max_proposal_size_(max_proposal_size), - proposal_delay_(proposal_delay), - vote_delay_(vote_delay), - is_mst_supported_(opt_mst_gossip_params), - mst_expiration_time_(mst_expiration_time), - max_rounds_delay_(max_rounds_delay), - stale_stream_max_rounds_(stale_stream_max_rounds), - opt_alternative_peers_(std::move(opt_alternative_peers)), + keypair_(keypair), + startup_wsv_sync_policy_(startup_wsv_sync_policy), grpc_channel_params_(std::move(grpc_channel_params)), opt_mst_gossip_params_(opt_mst_gossip_params), inter_peer_tls_config_(std::move(inter_peer_tls_config)), pending_txs_storage_init( std::make_unique()), - keypair(keypair), pg_opt_(std::move(pg_opt)), ordering_init(logger_manager->getLogger()), yac_init(std::make_unique()), @@ -180,35 +162,34 @@ Irohad::~Irohad() { Irohad::RunResult Irohad::init() { // clang-format off return initSettings() - | [this]{ return initValidatorsConfigs();} - | [this]{ return initBatchParser();} - | [this]{ return initValidators();} - | [this]{ return initWsvRestorer(); // Recover WSV from the existing ledger - // to be sure it is consistent - } - | [this]{ return restoreWsv();} - | [this]{ return validateKeypair();} - | [this]{ return initTlsCredentials();} - | [this]{ return initPeerCertProvider();} - | [this]{ return initClientFactory();} - | [this]{ return initCryptoProvider();} - | [this]{ return initNetworkClient();} - | [this]{ return initFactories();} - | [this]{ return initPersistentCache();} - | [this]{ return initOrderingGate();} - | [this]{ return initSimulator();} - | [this]{ return initConsensusCache();} - | [this]{ return initBlockLoader();} - | [this]{ return initConsensusGate();} - | [this]{ return initSynchronizer();} - | [this]{ return initPeerCommunicationService();} - | [this]{ return initStatusBus();} - | [this]{ return initMstProcessor();} - | [this]{ return initPendingTxsStorageWithCache();} - - // Torii - | [this]{ return initTransactionCommandService();} - | [this]{ return initQueryService();}; + | [this]{ return initValidatorsConfigs();} + | [this]{ return initBatchParser();} + | [this]{ return initValidators();} + // Recover WSV from the existing ledger to be sure it is consistent + | [this]{ return initWsvRestorer(); } + | [this]{ return restoreWsv();} + | [this]{ return validateKeypair();} + | [this]{ return initTlsCredentials();} + | [this]{ return initPeerCertProvider();} + | [this]{ return initClientFactory();} + | [this]{ return initCryptoProvider();} + | [this]{ return initNetworkClient();} + | [this]{ return initFactories();} + | [this]{ return initPersistentCache();} + | [this]{ return initOrderingGate();} + | [this]{ return initSimulator();} + | [this]{ return initConsensusCache();} + | [this]{ return initBlockLoader();} + | [this]{ return initConsensusGate();} + | [this]{ return initSynchronizer();} + | [this]{ return initPeerCommunicationService();} + | [this]{ return initStatusBus();} + | [this]{ return initMstProcessor();} + | [this]{ return initPendingTxsStorageWithCache();} + + // Torii + | [this]{ return initTransactionCommandService();} + | [this]{ return initQueryService();}; // clang-format on } @@ -246,13 +227,13 @@ Irohad::RunResult Irohad::initSettings() { Irohad::RunResult Irohad::initValidatorsConfigs() { validators_config_ = std::make_shared( - max_proposal_size_); + config_.max_proposal_size); block_validators_config_ = std::make_shared( - max_proposal_size_, true); + config_.max_proposal_size, true); proposal_validators_config_ = std::make_shared( - max_proposal_size_, false, true); + config_.max_proposal_size, false, true); log_->info("[Init] => validators configs"); return {}; } @@ -278,7 +259,7 @@ Irohad::RunResult Irohad::initStorage( pool_wrapper_, pending_txs_storage_, query_response_factory_, - block_store_dir_, + config_.block_store_path, vm_caller_ref, log_manager_->getChild("Storage")) | [&](auto &&v) -> RunResult { @@ -311,8 +292,11 @@ Irohad::RunResult Irohad::initStorage( } Irohad::RunResult Irohad::restoreWsv() { - return wsv_restorer_->restoreWsv(*storage) | - [](const auto &ledger_state) -> RunResult { + return wsv_restorer_->restoreWsv( + *storage, + startup_wsv_sync_policy_ + == StartupWsvSynchronizationPolicy::kWaitForNewBlocks) + | [](const auto &ledger_state) -> RunResult { assert(ledger_state); if (ledger_state->ledger_peers.empty()) { return iroha::expected::makeError( @@ -323,9 +307,11 @@ Irohad::RunResult Irohad::restoreWsv() { } Irohad::RunResult Irohad::validateKeypair() { + BOOST_ASSERT_MSG(keypair_.has_value(), "keypair must be specified somewhere"); + auto peers = storage->createPeerQuery() | [this](auto &&peer_query) { return peer_query->getLedgerPeerByPublicKey( - PublicKeyHexStringView{keypair.publicKey()}); + PublicKeyHexStringView{keypair_->publicKey()}); }; if (not peers) { log_->warn("There is no peer in the ledger with my public key!"); @@ -340,9 +326,10 @@ Irohad::RunResult Irohad::validateKeypair() { Irohad::RunResult Irohad::initTlsCredentials() { const auto &p2p_path = inter_peer_tls_config_ | [](const auto &p2p_config) { return p2p_config.my_tls_creds_path; }; - const auto &torii_path = torii_tls_params_ | [](const auto &torii_config) { - return boost::make_optional(torii_config.key_path); - }; + const auto &torii_path = + config_.torii_tls_params | [](const auto &torii_config) { + return boost::make_optional(torii_config.key_path); + }; auto load_tls_creds = [this](const auto &opt_path, const auto &description, @@ -434,7 +421,7 @@ Irohad::RunResult Irohad::initClientFactory() { */ Irohad::RunResult Irohad::initCryptoProvider() { crypto_signer_ = - std::make_shared>(keypair); + std::make_shared>(*keypair_); log_->info("[Init] => crypto provider"); return {}; @@ -609,20 +596,20 @@ Irohad::RunResult Irohad::initOrderingGate() { std::shared_ptr proposal_strategy = std::make_shared(); - ordering_gate = - ordering_init.initOrderingGate(max_proposal_size_, - proposal_delay_, - std::move(hashes), - transaction_factory, - batch_parser, - transaction_batch_factory_, - async_call_, - std::move(factory), - proposal_factory, - persistent_cache, - proposal_strategy, - log_manager_->getChild("Ordering"), - inter_peer_client_factory_); + ordering_gate = ordering_init.initOrderingGate( + config_.max_proposal_size, + std::chrono::milliseconds(config_.proposal_delay), + std::move(hashes), + transaction_factory, + batch_parser, + transaction_batch_factory_, + async_call_, + std::move(factory), + proposal_factory, + persistent_cache, + proposal_strategy, + log_manager_->getChild("Ordering"), + inter_peer_client_factory_); log_->info("[Init] => init ordering gate - [{}]", logger::boolRepr(bool(ordering_gate))); return {}; @@ -714,17 +701,18 @@ Irohad::RunResult Irohad::initConsensusGate() { consensus_gate = yac_init->initConsensusGate( {block->height(), ordering::kFirstRejectRound}, storage, - opt_alternative_peers_, + config_.initial_peers, *initial_ledger_state, simulator, block_loader, - keypair, + *keypair_, consensus_result_cache_, - vote_delay_, + std::chrono::milliseconds(config_.vote_delay), async_call_, kConsensusConsistencyModel, log_manager_->getChild("Consensus"), - max_rounds_delay_, + std::chrono::milliseconds( + config_.max_round_delay_ms.value_or(kMaxRoundsDelayDefault)), inter_peer_client_factory_); consensus_gate->onOutcome().subscribe( consensus_gate_events_subscription, @@ -798,7 +786,8 @@ Irohad::RunResult Irohad::initMstProcessor() { auto mst_logger_manager = log_manager_->getChild("MultiSignatureTransactions"); auto mst_state_logger = mst_logger_manager->getChild("State")->getLogger(); - auto mst_completer = std::make_shared(mst_expiration_time_); + auto mst_completer = std::make_shared(std::chrono::minutes( + config_.mst_expiration_time.value_or(kMstExpirationTimeDefault))); auto mst_storage = MstStorageStateImpl::create( mst_completer, finalized_txs_, @@ -806,7 +795,7 @@ Irohad::RunResult Irohad::initMstProcessor() { mst_logger_manager->getChild("Storage")->getLogger()); pending_txs_storage_init->setFinalizedTxsSubscription(finalized_txs_); std::shared_ptr mst_propagation; - if (is_mst_supported_) { + if (config_.mst_support) { mst_transport = std::make_shared( async_call_, transaction_factory, @@ -814,7 +803,7 @@ Irohad::RunResult Irohad::initMstProcessor() { transaction_batch_factory_, persistent_cache, mst_completer, - PublicKeyHexStringView{keypair.publicKey()}, + PublicKeyHexStringView{keypair_->publicKey()}, std::move(mst_state_logger), mst_logger_manager->getChild("Transport")->getLogger(), std::make_uniquegetChild("Transport")->getLogger()); log_->info("[Init] => command service"); @@ -922,7 +912,8 @@ Irohad::RunResult Irohad::initWsvRestorer() { wsv_restorer_ = std::make_shared( std::move(interface_validator), std::move(proto_validator), - chain_validator); + chain_validator, + log_manager_->getChild("WsvRestorer")->getLogger()); return {}; } @@ -935,13 +926,13 @@ Irohad::RunResult Irohad::run() { // Initializing torii server torii_server = std::make_unique( - listen_ip_ + ":" + std::to_string(torii_port_), + listen_ip_ + ":" + std::to_string(config_.torii_port), log_manager_->getChild("ToriiServerRunner")->getLogger(), false); // Initializing internal server internal_server = std::make_unique( - listen_ip_ + ":" + std::to_string(internal_port_), + listen_ip_ + ":" + std::to_string(config_.internal_port), log_manager_->getChild("InternalServerRunner")->getLogger(), false); @@ -962,7 +953,7 @@ Irohad::RunResult Irohad::run() { torii_tls_creds_ | [&, this](const auto &tls_creds) { run_result |= [&, this] { torii_tls_server = std::make_unique( - listen_ip_ + ":" + std::to_string(torii_tls_params_->port), + listen_ip_ + ":" + std::to_string(config_.torii_tls_params->port), log_manager_->getChild("ToriiTlsServerRunner")->getLogger(), false, tls_creds); @@ -976,7 +967,7 @@ Irohad::RunResult Irohad::run() { // Run internal server run_result |= [&, this] { - if (is_mst_supported_) { + if (config_.mst_support) { internal_server->append( std::static_pointer_cast(mst_transport)); } diff --git a/irohad/main/application.hpp b/irohad/main/application.hpp index 556eb5eac06..a8307b2f292 100644 --- a/irohad/main/application.hpp +++ b/irohad/main/application.hpp @@ -98,56 +98,30 @@ class Irohad { /** * Constructor that initializes common iroha pipeline - * @param block_store_dir - folder where blocks will be stored * @param pg_opt - connection options for PostgresSQL * @param listen_ip - ip address for opening ports (internal & torii) - * @param torii_port - port for torii binding - * @param internal_port - port for internal communication - ordering service, - * consensus, and block loader - * @param max_proposal_size - maximum transactions that possible appears in - * one proposal - * @param proposal_delay - maximum waiting time util emitting new proposal - * @param vote_delay - waiting time before sending vote to next peer - * @param mst_expiration_time - maximum time until until MST transaction is * not considered as expired (in minutes) * @param keypair - public and private keys for crypto signer - * @param max_rounds_delay - maximum delay between consecutive rounds without - * transactions - * @param stale_stream_max_rounds - maximum number of rounds between - * consecutive status emissions - * @param opt_alternative_peers - optional alternative initial peers list * @param logger_manager - the logger manager to use * @param startup_wsv_data_policy - @see StartupWsvDataPolicy * @param grpc_channel_params - parameters for all grpc clients * @param opt_mst_gossip_params - parameters for Gossip MST propagation * (optional). If not provided, disables mst processing support - * @param torii_tls_params - optional TLS params for torii. * @see iroha::torii::TlsParams * @param inter_peer_tls_config - set up TLS in peer-to-peer communication * TODO mboldyrev 03.11.2018 IR-1844 Refactor the constructor. */ - Irohad(const boost::optional &block_store_dir, + Irohad(const IrohadConfig &config, std::unique_ptr pg_opt, const std::string &listen_ip, - size_t torii_port, - size_t internal_port, - size_t max_proposal_size, - std::chrono::milliseconds proposal_delay, - std::chrono::milliseconds vote_delay, - std::chrono::minutes mst_expiration_time, - const shared_model::crypto::Keypair &keypair, - std::chrono::milliseconds max_rounds_delay, - size_t stale_stream_max_rounds, - boost::optional - opt_alternative_peers, + const boost::optional &keypair, logger::LoggerManagerTreePtr logger_manager, iroha::StartupWsvDataPolicy startup_wsv_data_policy, + iroha::StartupWsvSynchronizationPolicy startup_wsv_sync_policy, std::shared_ptr grpc_channel_params, const boost::optional - &opt_mst_gossip_params = boost::none, - const boost::optional &torii_tls_params = - boost::none, + &opt_mst_gossip_params, boost::optional inter_peer_tls_config = boost::none); @@ -241,20 +215,10 @@ class Irohad { virtual RunResult initWsvRestorer(); // constructor dependencies - const boost::optional block_store_dir_; + IrohadConfig config_; const std::string listen_ip_; - size_t torii_port_; - boost::optional torii_tls_params_; - size_t internal_port_; - size_t max_proposal_size_; - std::chrono::milliseconds proposal_delay_; - std::chrono::milliseconds vote_delay_; - bool is_mst_supported_; - std::chrono::minutes mst_expiration_time_; - std::chrono::milliseconds max_rounds_delay_; - size_t stale_stream_max_rounds_; - const boost::optional - opt_alternative_peers_; + boost::optional keypair_; + iroha::StartupWsvSynchronizationPolicy startup_wsv_sync_policy_; std::shared_ptr grpc_channel_params_; boost::optional opt_mst_gossip_params_; @@ -282,7 +246,6 @@ class Irohad { std::optional> vm_caller_; public: - shared_model::crypto::Keypair keypair; std::unique_ptr pg_opt_; std::shared_ptr storage; diff --git a/irohad/main/irohad.cpp b/irohad/main/irohad.cpp index 8694653ecd0..8e8ca34e402 100644 --- a/irohad/main/irohad.cpp +++ b/irohad/main/irohad.cpp @@ -3,13 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include +#include + #include #include #include #include -#include -#include #include "ametsuchi/storage.hpp" #include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" #include "common/bind.hpp" @@ -39,9 +40,6 @@ static const std::string kListenIp = "0.0.0.0"; static const std::string kLogSettingsFromConfigFile = "config_file"; -static const uint32_t kMstExpirationTimeDefault = 1440; -static const uint32_t kMaxRoundsDelayDefault = 3000; -static const uint32_t kStaleStreamMaxRoundsDefault = 2; static const std::string kDefaultWorkingDatabaseName{"iroha_default"}; static const std::chrono::milliseconds kExitCheckPeriod{1000}; @@ -65,7 +63,27 @@ DEFINE_string(keypair_name, "", "Specify name of .pub and .priv files"); */ DEFINE_bool(overwrite_ledger, false, "Overwrite ledger data if existing"); -DEFINE_bool(reuse_state, false, "Try to reuse existing state data at startup."); +/** + * Startup option to reuse existing WSV. Ignored since state is reused by + * default. + */ +DEFINE_bool(reuse_state, + true, + "Try to reuse existing state data at startup (Deprecated, startup " + "reuses state by default. Use drop_state to drop the WSV)."); + +/** + * Startup option to drop existing WSV. Cannot be used with 'reuse_state'. + */ +DEFINE_bool(drop_state, false, "Drops existing state data at startup."); + +/** + * Startup option for WSV synchronization mode. + */ +DEFINE_bool(wait_for_new_blocks, + false, + "Startup synchronization policy - waits for new blocks in " + "blockstore, does not run network"); static bool validateVerbosity(const char *flagname, const std::string &val) { if (val == kLogSettingsFromConfigFile) { @@ -223,29 +241,29 @@ int main(int argc, char *argv[]) { log->critical( "Got an empty initial peers list in configuration file. You have to " "either specify some peers or avoid overriding the peers from " - "genesis " - "block!"); + "genesis block!"); return EXIT_FAILURE; } if (config.utility_service) { - initUtilityService(config.utility_service.value(), - [] { - exit_requested.set_value(); - std::lock_guard{shutdown_wait_mutex}; - }, - log_manager); + initUtilityService( + config.utility_service.value(), + [] { + exit_requested.set_value(); + std::lock_guard{shutdown_wait_mutex}; + }, + log_manager); } daemon_status_notifier->notify( ::iroha::utility_service::Status::kInitialization); - BOOST_ASSERT_MSG(not FLAGS_keypair_name.empty() or config.crypto, - "keypair must be specified somewhere"); - - auto keypair = FLAGS_keypair_name.empty() - ? getKeypairFromConfig(config.crypto.value()) - : getKeypairFromFile(FLAGS_keypair_name, log_manager); + boost::optional keypair = boost::none; + if (!FLAGS_keypair_name.empty()) { + keypair = getKeypairFromFile(FLAGS_keypair_name, log_manager); + } else if (config.crypto.has_value()) { + keypair = getKeypairFromConfig(config.crypto.value()); + } std::unique_ptr pg_opt; if (config.database_config) { @@ -269,29 +287,20 @@ int main(int argc, char *argv[]) { // Configuring iroha daemon auto irohad = std::make_unique( - config.block_store_path, + config, std::move(pg_opt), kListenIp, // TODO(mboldyrev) 17/10/2018: add a parameter in // config file and/or command-line arguments? - config.torii_port, - config.internal_port, - config.max_proposal_size, - std::chrono::milliseconds(config.proposal_delay), - std::chrono::milliseconds(config.vote_delay), - std::chrono::minutes( - config.mst_expiration_time.value_or(kMstExpirationTimeDefault)), std::move(keypair), - std::chrono::milliseconds( - config.max_round_delay_ms.value_or(kMaxRoundsDelayDefault)), - config.stale_stream_max_rounds.value_or(kStaleStreamMaxRoundsDefault), - std::move(config.initial_peers), log_manager->getChild("Irohad"), - FLAGS_reuse_state ? iroha::StartupWsvDataPolicy::kReuse - : iroha::StartupWsvDataPolicy::kDrop, + FLAGS_drop_state ? iroha::StartupWsvDataPolicy::kDrop + : iroha::StartupWsvDataPolicy::kReuse, + FLAGS_wait_for_new_blocks + ? iroha::StartupWsvSynchronizationPolicy::kWaitForNewBlocks + : iroha::StartupWsvSynchronizationPolicy::kSyncUpAndGo, ::iroha::network::getDefaultChannelParams(), boost::make_optional(config.mst_support, iroha::GossipPropagationStrategyParams{}), - config.torii_tls_params, boost::none); // Check if iroha daemon storage was successfully initialized diff --git a/irohad/main/startup_params.hpp b/irohad/main/startup_params.hpp index b6476a3850c..e4a1eeab138 100644 --- a/irohad/main/startup_params.hpp +++ b/irohad/main/startup_params.hpp @@ -7,11 +7,22 @@ #define IROHA_STARTUP_PARAMS_HPP namespace iroha { - /// Policy regarging possible existing WSV data at startup + /** + * Policy regarging possible existing WSV data at startup + */ enum class StartupWsvDataPolicy { - kReuse, //!< try to reuse existing data in the + kReuse, //!< try to reuse existing data in the WSV kDrop, //!< drop any existing state data }; + + /** + * Startup synchronization policy + */ + enum class StartupWsvSynchronizationPolicy { + kSyncUpAndGo, //!< sync up and continue execution + kWaitForNewBlocks, //!< enter endless loop to wait for new blocks added + //!< externally + }; } // namespace iroha #endif diff --git a/test/framework/integration_framework/integration_test_framework.cpp b/test/framework/integration_framework/integration_test_framework.cpp index f5191c68f14..3273f0987a3 100644 --- a/test/framework/integration_framework/integration_test_framework.cpp +++ b/test/framework/integration_framework/integration_test_framework.cpp @@ -5,15 +5,15 @@ #include "framework/integration_framework/integration_test_framework.hpp" -#include -#include - #include #include +#include +#include #include #include #include #include + #include "ametsuchi/storage.hpp" #include "backend/protobuf/block.hpp" #include "backend/protobuf/common_objects/proto_common_objects_factory.hpp" @@ -166,17 +166,6 @@ namespace integration_framework { block_queue_(std::make_unique>(block_waiting)), port_guard_(std::make_unique()), torii_port_(port_guard_->getPort(kDefaultToriiPort)), - internal_port_(port_guard_->getPort(kDefaultInternalPort)), - iroha_instance_( - std::make_shared(mst_support, - block_store_path, - kLocalHost, - torii_port_, - internal_port_, - log_manager_->getChild("Irohad"), - log_, - startup_wsv_data_policy, - dbname)), command_client_(std::make_unique( iroha::network::createInsecureClient< torii::CommandSyncClient::Service>( @@ -233,7 +222,30 @@ namespace integration_framework { makeTransportClientFactory( client_factory_), log_manager_->getChild("ConsensusTransport")->getLogger())), - cleanup_on_exit_(cleanup_on_exit) {} + cleanup_on_exit_(cleanup_on_exit) { + // 1 h proposal_timeout results in non-deterministic behavior due to thread + // scheduling and network + config_.proposal_delay = 3600000; + // 100 ms is small delay to avoid unnecessary messages due to eternal voting + // and to allow scheduler to switch threads + config_.vote_delay = 100; + // amount of minutes in a day + config_.mst_expiration_time = 24 * 60; + config_.max_round_delay_ms = 0; + config_.stale_stream_max_rounds = 2; + config_.max_proposal_size = 10; + config_.mst_support = mst_support; + config_.block_store_path = block_store_path; + config_.torii_port = torii_port_; + config_.internal_port = port_guard_->getPort(kDefaultInternalPort); + iroha_instance_ = + std::make_shared(config_, + kLocalHost, + log_manager_->getChild("Irohad"), + log_, + startup_wsv_data_policy, + dbname); + } IntegrationTestFramework::~IntegrationTestFramework() { if (cleanup_on_exit_) { @@ -468,7 +480,7 @@ namespace integration_framework { } std::string IntegrationTestFramework::getAddress() const { - return format_address(kLocalHost, internal_port_); + return format_address(kLocalHost, config_.internal_port); } rxcpp::observable> @@ -700,10 +712,9 @@ namespace integration_framework { async_call_, proposal_factory_, [] { return std::chrono::system_clock::now(); }, - std::chrono::milliseconds(0), // the proposal waiting timeout is - // only used when waiting a response - // for a proposal request, which our - // client does not do + // the proposal waiting timeout is only used when waiting a response + // for a proposal request, which our client does not do + std::chrono::milliseconds(0), log_manager_->getChild("OrderingClientTransport")->getLogger(), makeTransportClientFactory< iroha::ordering::transport::OnDemandOsClientGrpcFactory>( @@ -822,7 +833,7 @@ namespace integration_framework { } size_t IntegrationTestFramework::internalPort() const { - return internal_port_; + return config_.internal_port; } void IntegrationTestFramework::done() { diff --git a/test/framework/integration_framework/integration_test_framework.hpp b/test/framework/integration_framework/integration_test_framework.hpp index a059a38b3f4..5baf96f4807 100644 --- a/test/framework/integration_framework/integration_test_framework.hpp +++ b/test/framework/integration_framework/integration_test_framework.hpp @@ -10,13 +10,14 @@ #include #include #include - #include + #include "consensus/gate_object.hpp" #include "cryptography/keypair.hpp" #include "interfaces/common_objects/string_view_types.hpp" #include "logger/logger_fwd.hpp" #include "logger/logger_manager_fwd.hpp" +#include "main/iroha_conf_loader.hpp" #include "main/startup_params.hpp" #include "synchronizer/synchronizer_common.hpp" @@ -510,7 +511,7 @@ namespace integration_framework { std::unique_ptr port_guard_; size_t torii_port_; - size_t internal_port_; + IrohadConfig config_; std::shared_ptr iroha_instance_; std::unique_ptr command_client_; std::unique_ptr query_client_; diff --git a/test/framework/integration_framework/iroha_instance.cpp b/test/framework/integration_framework/iroha_instance.cpp index 0367f6967ec..a3e6e76ea65 100644 --- a/test/framework/integration_framework/iroha_instance.cpp +++ b/test/framework/integration_framework/iroha_instance.cpp @@ -5,10 +5,10 @@ #include "framework/integration_framework/iroha_instance.hpp" +#include #include #include -#include #include "ametsuchi/impl/postgres_options.hpp" #include "ametsuchi/storage.hpp" #include "cryptography/keypair.hpp" @@ -24,39 +24,22 @@ static constexpr std::chrono::milliseconds kMstEmissionPeriod = 100ms; namespace integration_framework { IrohaInstance::IrohaInstance( - bool mst_support, - const boost::optional &block_store_path, + const IrohadConfig &config, const std::string &listen_ip, - size_t torii_port, - size_t internal_port, logger::LoggerManagerTreePtr irohad_log_manager, logger::LoggerPtr log, iroha::StartupWsvDataPolicy startup_wsv_data_policy, - const boost::optional &dbname, - const boost::optional &torii_tls_params) - : block_store_dir_(block_store_path), + const boost::optional &dbname) + : config_(config), working_dbname_(dbname.value_or(getRandomDbName())), listen_ip_(listen_ip), - torii_port_(torii_port), - torii_tls_params_(torii_tls_params), - internal_port_(internal_port), - // proposal_timeout results in non-deterministic behavior due - // to thread scheduling and network - proposal_delay_(1h), - // small delay to avoid unnecessary messages due to eternal voting - // and to allow scheduler to switch threads - vote_delay_(100ms), - // amount of minutes in a day - mst_expiration_time_(std::chrono::minutes(24 * 60)), opt_mst_gossip_params_(boost::make_optional( - mst_support, + config_.mst_support, [] { iroha::GossipPropagationStrategyParams params; params.emission_period = kMstEmissionPeriod; return params; }())), - max_rounds_delay_(0ms), - stale_stream_max_rounds_(2), irohad_log_manager_(std::move(irohad_log_manager)), log_(std::move(log)), startup_wsv_data_policy_(startup_wsv_data_policy) {} @@ -103,26 +86,17 @@ namespace integration_framework { void IrohaInstance::initPipeline( const shared_model::crypto::Keypair &key_pair, size_t max_proposal_size) { + config_.max_proposal_size = max_proposal_size; instance_ = std::make_shared( - block_store_dir_, + config_, std::make_unique( getPostgresCredsOrDefault(), working_dbname_, log_), listen_ip_, - torii_port_, - internal_port_, - max_proposal_size, - proposal_delay_, - vote_delay_, - mst_expiration_time_, key_pair, - max_rounds_delay_, - stale_stream_max_rounds_, - boost::none, irohad_log_manager_, log_, startup_wsv_data_policy_, - opt_mst_gossip_params_, - torii_tls_params_); + opt_mst_gossip_params_); } void IrohaInstance::run() { @@ -147,8 +121,8 @@ namespace integration_framework { instance_.reset(); log_->info("removing storage"); iroha::ametsuchi::PgConnectionInit::dropWorkingDatabase(pg_opt); - if (block_store_dir_) { - boost::filesystem::remove_all(block_store_dir_.value()); + if (config_.block_store_path) { + boost::filesystem::remove_all(config_.block_store_path.value()); } } diff --git a/test/framework/integration_framework/iroha_instance.hpp b/test/framework/integration_framework/iroha_instance.hpp index c9151574a79..48d5838d113 100644 --- a/test/framework/integration_framework/iroha_instance.hpp +++ b/test/framework/integration_framework/iroha_instance.hpp @@ -6,16 +6,17 @@ #ifndef IROHA_IROHA_INSTANCE_HPP #define IROHA_IROHA_INSTANCE_HPP +#include +#include +#include #include #include #include -#include -#include -#include #include "ametsuchi/impl/postgres_options.hpp" #include "logger/logger_fwd.hpp" #include "logger/logger_manager_fwd.hpp" +#include "main/iroha_conf_loader.hpp" #include "main/startup_params.hpp" #include "multi_sig_transactions/gossip_propagation_strategy_params.hpp" #include "torii/tls_params.hpp" @@ -36,29 +37,18 @@ namespace integration_framework { class IrohaInstance { public: /** - * @param mst_support enables multisignature tx support - * @param block_store_path * @param listen_ip - ip address for opening ports (internal & torii) - * @param torii_port - port to bind Torii service to - * @param internal_port - port for internal irohad communication * @param irohad_log_manager - the log manager for irohad * @param log - the log for internal messages * @param startup_wsv_data_policy - @see StartupWsvDataPolicy * @param dbname is a name of postgres database - * @param tls_params - optional tls parameters for torii - * @see iroha::torii::TlsParams */ - IrohaInstance(bool mst_support, - const boost::optional &block_store_path, + IrohaInstance(const IrohadConfig &config, const std::string &listen_ip, - size_t torii_port, - size_t internal_port, logger::LoggerManagerTreePtr irohad_log_manager, logger::LoggerPtr log, iroha::StartupWsvDataPolicy startup_wsv_data_policy, - const boost::optional &dbname = boost::none, - const boost::optional &tls_params = - boost::none); + const boost::optional &dbname = boost::none); /// Initialize Irohad. Throws on error. void init(); @@ -86,19 +76,11 @@ namespace integration_framework { void terminateAndCleanup(); // config area - const boost::optional block_store_dir_; + IrohadConfig config_; const std::string working_dbname_; const std::string listen_ip_; - const size_t torii_port_; - boost::optional torii_tls_params_; - const size_t internal_port_; - const std::chrono::milliseconds proposal_delay_; - const std::chrono::milliseconds vote_delay_; - const std::chrono::minutes mst_expiration_time_; boost::optional opt_mst_gossip_params_; - const std::chrono::milliseconds max_rounds_delay_; - const size_t stale_stream_max_rounds_; private: std::shared_ptr instance_; diff --git a/test/framework/integration_framework/test_irohad.hpp b/test/framework/integration_framework/test_irohad.hpp index 9389074d529..2d5dee8c1d8 100644 --- a/test/framework/integration_framework/test_irohad.hpp +++ b/test/framework/integration_framework/test_irohad.hpp @@ -17,45 +17,24 @@ namespace integration_framework { */ class TestIrohad : public Irohad { public: - TestIrohad(const boost::optional &block_store_dir, + TestIrohad(const IrohadConfig &config, std::unique_ptr pg_opt, const std::string &listen_ip, - size_t torii_port, - size_t internal_port, - size_t max_proposal_size, - std::chrono::milliseconds proposal_delay, - std::chrono::milliseconds vote_delay, - std::chrono::minutes mst_expiration_time, const shared_model::crypto::Keypair &keypair, - std::chrono::milliseconds max_rounds_delay, - size_t stale_stream_max_rounds, - boost::optional - opt_alternative_peers, logger::LoggerManagerTreePtr irohad_log_manager, logger::LoggerPtr log, iroha::StartupWsvDataPolicy startup_wsv_data_policy, const boost::optional - &opt_mst_gossip_params = boost::none, - const boost::optional - &torii_tls_params = boost::none) - : Irohad(block_store_dir, + &opt_mst_gossip_params = boost::none) + : Irohad(config, std::move(pg_opt), listen_ip, - torii_port, - internal_port, - max_proposal_size, - proposal_delay, - vote_delay, - mst_expiration_time, keypair, - max_rounds_delay, - stale_stream_max_rounds, - std::move(opt_alternative_peers), std::move(irohad_log_manager), startup_wsv_data_policy, + iroha::StartupWsvSynchronizationPolicy::kSyncUpAndGo, iroha::network::getDefaultTestChannelParams(), opt_mst_gossip_params, - torii_tls_params, boost::none), log_(std::move(log)) {} diff --git a/test/module/irohad/ametsuchi/ametsuchi_test.cpp b/test/module/irohad/ametsuchi/ametsuchi_test.cpp index c09eea1e8d2..ffd1e20fa7d 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_test.cpp +++ b/test/module/irohad/ametsuchi/ametsuchi_test.cpp @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" - #include #include "ametsuchi/impl/flat_file/flat_file.hpp" @@ -21,6 +19,7 @@ #include "framework/result_gtest_checkers.hpp" #include "framework/test_logger.hpp" #include "framework/test_subscriber.hpp" +#include "module/irohad/ametsuchi/ametsuchi_fixture.hpp" #include "module/shared_model/builders/protobuf/test_block_builder.hpp" #include "module/shared_model/builders/protobuf/test_transaction_builder.hpp" #include "module/shared_model/cryptography/crypto_defaults.hpp" @@ -495,12 +494,13 @@ TEST_F(AmetsuchiTest, TestRestoreWsvFromBlockStorage) { auto proto_validator = std::make_unique(); WsvRestorerImpl wsvRestorer(std::move(interface_validator), std::move(proto_validator), - chain_validator); - wsvRestorer.restoreWsv(*storage).match([](const auto &) {}, - [&](const auto &error) { - FAIL() << "Failed to recover WSV: " - << error.error; - }); + chain_validator, + getTestLogger("WsvRestorer")); + wsvRestorer.restoreWsv(*storage, false) + .match([](const auto &) {}, + [&](const auto &error) { + FAIL() << "Failed to recover WSV: " << error.error; + }); res = sql_query->getDomain(kDomain); EXPECT_TRUE(res); @@ -543,12 +543,13 @@ class RestoreWsvTest : public AmetsuchiTest { auto proto_validator = std::make_unique(); WsvRestorerImpl wsvRestorer(std::move(interface_validator), std::move(proto_validator), - chain_validator); - wsvRestorer.restoreWsv(*storage).match([](const auto &) {}, - [&](const auto &error) { - FAIL() << "Failed to recover WSV: " - << error.error; - }); + chain_validator, + getTestLogger("WsvRestorer")); + wsvRestorer.restoreWsv(*storage, false) + .match([](const auto &) {}, + [&](const auto &error) { + FAIL() << "Failed to recover WSV: " << error.error; + }); } void checkRestoreWsvError(const std::string error_substr) { @@ -557,12 +558,16 @@ class RestoreWsvTest : public AmetsuchiTest { auto proto_validator = std::make_unique(); WsvRestorerImpl wsvRestorer(std::move(interface_validator), std::move(proto_validator), - chain_validator); - wsvRestorer.restoreWsv(*storage).match( - [](const auto &) { FAIL() << "Should have failed to recover WSV."; }, - [&](const auto &error) { - EXPECT_THAT(error.error, ::testing::HasSubstr(error_substr)); - }); + chain_validator, + getTestLogger("WsvRestorer")); + wsvRestorer.restoreWsv(*storage, false) + .match( + [](const auto &) { + FAIL() << "Should have failed to recover WSV."; + }, + [&](const auto &error) { + EXPECT_THAT(error.error, ::testing::HasSubstr(error_substr)); + }); } }; diff --git a/test/module/irohad/ametsuchi/mock_block_query.hpp b/test/module/irohad/ametsuchi/mock_block_query.hpp index 664827c313d..6f7a209d89c 100644 --- a/test/module/irohad/ametsuchi/mock_block_query.hpp +++ b/test/module/irohad/ametsuchi/mock_block_query.hpp @@ -24,6 +24,7 @@ namespace iroha { (override)); MOCK_METHOD0(getTopBlockHeight, shared_model::interface::types::HeightType()); + MOCK_METHOD0(reloadBlockstore, void()); }; } // namespace ametsuchi diff --git a/test/module/irohad/ametsuchi/mock_block_storage.hpp b/test/module/irohad/ametsuchi/mock_block_storage.hpp index cc0c1d523bf..3932cea8bbf 100644 --- a/test/module/irohad/ametsuchi/mock_block_storage.hpp +++ b/test/module/irohad/ametsuchi/mock_block_storage.hpp @@ -6,10 +6,10 @@ #ifndef IROHA_MOCK_BLOCK_STORAGE_HPP #define IROHA_MOCK_BLOCK_STORAGE_HPP -#include "ametsuchi/block_storage.hpp" - #include +#include "ametsuchi/block_storage.hpp" + namespace iroha { namespace ametsuchi { class MockBlockStorage : public BlockStorage { @@ -20,8 +20,9 @@ namespace iroha { fetch, boost::optional>( shared_model::interface::types::HeightType)); - MOCK_CONST_METHOD0(size, size_t(void)); - MOCK_METHOD0(clear, void(void)); + MOCK_CONST_METHOD0(size, size_t()); + MOCK_METHOD0(reload, void()); + MOCK_METHOD0(clear, void()); MOCK_CONST_METHOD1(forEach, expected::Result(FunctionType)); }; diff --git a/test/system/irohad_test.cpp b/test/system/irohad_test.cpp index 9964e6ca1ae..a7a69ffd40d 100644 --- a/test/system/irohad_test.cpp +++ b/test/system/irohad_test.cpp @@ -3,11 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include - -#include "util/proto_status_tools.hpp" - #include #include #include @@ -15,10 +10,13 @@ #include #include #include + #include #include #include #include +#include +#include #include "ametsuchi/impl/postgres_options.hpp" #include "backend/protobuf/proto_block_json_converter.hpp" @@ -42,6 +40,7 @@ #include "network/impl/channel_factory.hpp" #include "torii/command_client.hpp" #include "torii/query_client.hpp" +#include "util/proto_status_tools.hpp" #include "util/utility_client.hpp" // workaround for Windows includes which redefine GetObject @@ -110,7 +109,7 @@ class IrohadTest : public AcceptanceFixture { db_name_ = integration_framework::getRandomDbName(); pgopts_ = "dbname=" + db_name_ + " " + integration_framework::getPostgresCredsFromEnv().value_or( - doc[config_members::PgOpt].GetString()); + doc[config_members::PgOpt].GetString()); // we need a separate file here in case if target environment // has custom database connection options set // via environment variables @@ -231,8 +230,10 @@ class IrohadTest : public AcceptanceFixture { } std::string setDefaultParams() { - return params( - config_copy_, path_genesis_.string(), path_keypair_node_.string(), {}); + return params(config_copy_, + path_genesis_.string(), + path_keypair_node_.string(), + std::string{"--drop_state"}); } static const iroha::network::GrpcChannelParams &getChannelParams() { @@ -254,10 +255,10 @@ class IrohadTest : public AcceptanceFixture { auto client = enable_tls ? iroha::network::createSecureClient( - kAddress, port, root_ca_, boost::none, getChannelParams()) + kAddress, port, root_ca_, boost::none, getChannelParams()) : iroha::network::createInsecureClient< - torii::CommandSyncClient::Service>( - kAddress, port, getChannelParams()); + torii::CommandSyncClient::Service>( + kAddress, port, getChannelParams()); return torii::CommandSyncClient( std::move(client), @@ -604,7 +605,7 @@ TEST_F(IrohadTest, RestartWithOverwriteLedger) { launchIroha(config_copy_, path_genesis_.string(), path_keypair_node_.string(), - std::string("--overwrite-ledger")); + std::string("--overwrite-ledger --drop_state")); ASSERT_EQ(getBlockCount(), 1); @@ -687,7 +688,7 @@ TEST_F(IrohadTest, StartWithoutConfigAndKeyFile) { launchIroha(params(boost::none, path_genesis_.string(), boost::none, - std::string{"--verbosity=trace"}), + std::string{"--verbosity=trace --drop_state"}), env); auto key_pair = keys_manager_admin_.loadKeys(boost::none); @@ -733,7 +734,7 @@ TEST_F(IrohadTest, StartWithConfigAndEnvironmentParams) { launchIroha(params(config_copy_, path_genesis_.string(), path_keypair_node_.string(), - std::string{"--verbosity=trace"}), + std::string{"--verbosity=trace --drop_state"}), env); auto key_pair = keys_manager_admin_.loadKeys(boost::none); From 42bd8642ae3fb9c0c475692aeffa0d48a2245261 Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Wed, 3 Mar 2021 12:11:18 +0300 Subject: [PATCH 05/86] Feature/Improves stability in networks with faulty nodes (#809) * fixup! Signed-off-by: iceseer * fixup! Signed-off-by: iceseer * fixup! Signed-off-by: iceseer * tests fixup! Signed-off-by: iceseer Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- irohad/consensus/yac/impl/yac.cpp | 24 +++++++++++++++++-- .../yac/storage/yac_vote_storage.hpp | 10 ++++---- irohad/consensus/yac/yac.hpp | 2 +- .../consensus/yac/yac_rainy_day_test.cpp | 2 +- .../yac/yac_simple_cold_case_test.cpp | 2 +- .../consensus/yac/yac_sunny_day_test.cpp | 2 +- 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/irohad/consensus/yac/impl/yac.cpp b/irohad/consensus/yac/impl/yac.cpp index c568b58e924..41a988bc627 100644 --- a/irohad/consensus/yac/impl/yac.cpp +++ b/irohad/consensus/yac/impl/yac.cpp @@ -183,7 +183,8 @@ namespace iroha { // ------|Private interface|------ - void Yac::votingStep(VoteMessage vote) { + void Yac::votingStep(VoteMessage vote, uint32_t attempt) { + log_->info("votingStep got vote: {}, attempt {}", vote, attempt); std::unique_lock lock(mutex_); auto committed = vote_storage_.isCommitted(vote.hash.vote_round); @@ -191,6 +192,23 @@ namespace iroha { return; } + enum { kRotatePeriod = 10 }; + + if (0 != attempt && 0 == (attempt % kRotatePeriod)) { + vote_storage_.remove(vote.hash.vote_round); + } + + /** + * 3 attempts to build and commit block before we think that round is + * freezed + */ + if (attempt == kRotatePeriod) { + vote.hash.vote_hashes.proposal_hash.clear(); + vote.hash.vote_hashes.block_hash.clear(); + vote.hash.block_signature.reset(); + vote = crypto_->getVote(vote.hash); + } + auto &cluster_order = getCurrentOrder(); const auto ¤t_leader = cluster_order.currentLeader(); @@ -200,7 +218,9 @@ namespace iroha { propagateStateDirectly(current_leader, {vote}); cluster_order.switchToNext(); lock.unlock(); - timer_->invokeAfterDelay([this, vote] { this->votingStep(vote); }); + + timer_->invokeAfterDelay( + [this, vote, attempt] { this->votingStep(vote, attempt + 1); }); } void Yac::closeRound() { diff --git a/irohad/consensus/yac/storage/yac_vote_storage.hpp b/irohad/consensus/yac/storage/yac_vote_storage.hpp index fbaee797db9..fc1567aadd4 100644 --- a/irohad/consensus/yac/storage/yac_vote_storage.hpp +++ b/irohad/consensus/yac/storage/yac_vote_storage.hpp @@ -84,11 +84,6 @@ namespace iroha { findProposalStorage(const VoteMessage &msg, PeersNumberType peers_in_round); - /** - * Remove proposal storage by round - */ - void remove(const Round &round); - public: // --------| public api |-------- @@ -119,6 +114,11 @@ namespace iroha { */ bool isCommitted(const Round &round); + /** + * Remove proposal storage by round + */ + void remove(const Round &round); + /** * Method provide state of processing for concrete proposal/block * @param round, in which that proposal/block is being voted diff --git a/irohad/consensus/yac/yac.hpp b/irohad/consensus/yac/yac.hpp index 795cc7f71f0..47d9a0c8197 100644 --- a/irohad/consensus/yac/yac.hpp +++ b/irohad/consensus/yac/yac.hpp @@ -77,7 +77,7 @@ namespace iroha { * Voting step is strategy of propagating vote * until commit/reject message received */ - void votingStep(VoteMessage vote); + void votingStep(VoteMessage vote, uint32_t attempt = 0); /** * Erase temporary data of current round diff --git a/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp b/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp index 93039f0ac5f..34426bbb989 100644 --- a/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp +++ b/test/module/irohad/consensus/yac/yac_rainy_day_test.cpp @@ -17,7 +17,7 @@ using ::testing::Return; using namespace iroha::consensus::yac; using namespace framework::test_subscriber; -static constexpr size_t kFixedRandomNumber = 24; +static constexpr size_t kFixedRandomNumber = 9; /** * @given yac consensus with 4 peers diff --git a/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp b/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp index 4ee8c5bb493..54340785e4c 100644 --- a/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp +++ b/test/module/irohad/consensus/yac/yac_simple_cold_case_test.cpp @@ -29,7 +29,7 @@ using namespace framework::test_subscriber; using namespace std; using namespace shared_model::interface::types; -static constexpr size_t kRandomFixedNumber = 27; +static constexpr size_t kRandomFixedNumber = 9; /** * @given Yac and ordering over some peers diff --git a/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp b/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp index 541b2be660b..61ebcd321ba 100644 --- a/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp +++ b/test/module/irohad/consensus/yac/yac_sunny_day_test.cpp @@ -19,7 +19,7 @@ using namespace iroha::consensus::yac; using namespace framework::test_subscriber; using namespace std; -static constexpr size_t kRandomFixedNumber = 42; +static constexpr size_t kRandomFixedNumber = 9; /** * @given yac & 4 peers From b4a9cc911c63c9bedc6411ef9911810c035ca30b Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Tue, 9 Mar 2021 12:36:08 +0300 Subject: [PATCH 06/86] fix/Block request timeout. (#840) * fixup! Signed-off-by: iceseer * fixup! Signed-off-by: iceseer Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- irohad/ametsuchi/impl/flat_file/flat_file.cpp | 2 +- irohad/network/impl/block_loader_impl.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.cpp b/irohad/ametsuchi/impl/flat_file/flat_file.cpp index b4499175edf..cbd89e2501e 100644 --- a/irohad/ametsuchi/impl/flat_file/flat_file.cpp +++ b/irohad/ametsuchi/impl/flat_file/flat_file.cpp @@ -20,8 +20,8 @@ #include "logger/logger.hpp" #ifdef _WIN32 -#include #include +#include #endif using namespace iroha::ametsuchi; diff --git a/irohad/network/impl/block_loader_impl.cpp b/irohad/network/impl/block_loader_impl.cpp index 6262429bbd2..e0455913e7e 100644 --- a/irohad/network/impl/block_loader_impl.cpp +++ b/irohad/network/impl/block_loader_impl.cpp @@ -48,6 +48,8 @@ BlockLoaderImpl::retrieveBlocks( [height, shared_client, block_factory = block_factory_]( auto subscriber) { grpc::ClientContext context; + context.set_deadline(std::chrono::system_clock::now() + std::chrono::minutes(1ull)); + proto::BlockRequest request; request.set_height(height + 1); // request next block to our top From e321a30e4d9f2f1eefc3f8562e06e90a78b95f3d Mon Sep 17 00:00:00 2001 From: Bulat Saifullin Date: Fri, 5 Mar 2021 17:35:18 +0300 Subject: [PATCH 07/86] [DOPS-1289] Rename master branch (#804) * fix main branch Signed-off-by: Bulat Saifullin * Update doxygen.groovy Signed-off-by: Bulat Saifullin * rename master to main Signed-off-by: Bulat Saifullin --- .jenkinsci/build.groovy | 2 +- .jenkinsci/builders/x64-linux-build-steps.groovy | 2 +- .jenkinsci/text-variables.groovy | 8 ++++---- Jenkinsfile | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.jenkinsci/build.groovy b/.jenkinsci/build.groovy index 60bb724f245..86c1bad47ee 100644 --- a/.jenkinsci/build.groovy +++ b/.jenkinsci/build.groovy @@ -50,7 +50,7 @@ def sonarScanner(scmVars, environment) { -Dsonar.projectVersion=${BUILD_TAG} \ -Dsonar.github.oauth=${SORABOT_TOKEN} ${sonar_option} """ - if (scmVars.GIT_BRANCH == "master" ) + if (scmVars.GIT_BRANCH == "main" ) // push analysis results to sonar sh """ sonar-scanner \ diff --git a/.jenkinsci/builders/x64-linux-build-steps.groovy b/.jenkinsci/builders/x64-linux-build-steps.groovy index 7efc1928822..afc330495fb 100644 --- a/.jenkinsci/builders/x64-linux-build-steps.groovy +++ b/.jenkinsci/builders/x64-linux-build-steps.groovy @@ -83,7 +83,7 @@ def buildSteps(int parallelism, String compiler, String build_type, boolean buil """ postgresIP = sh(script: "docker inspect ${env.IROHA_POSTGRES_HOST} --format '{{ (index .NetworkSettings.Networks \"${env.IROHA_NETWORK}\").IPAddress }}'", returnStdout: true).trim() - def referenceBranchOrCommit = 'master' + def referenceBranchOrCommit = 'main' if (scmVars.GIT_LOCAL_BRANCH == referenceBranchOrCommit && scmVars.GIT_PREVIOUS_COMMIT) { referenceBranchOrCommit = scmVars.GIT_PREVIOUS_COMMIT } diff --git a/.jenkinsci/text-variables.groovy b/.jenkinsci/text-variables.groovy index 22faad61fef..c44acb716bf 100644 --- a/.jenkinsci/text-variables.groovy +++ b/.jenkinsci/text-variables.groovy @@ -202,7 +202,7 @@ cmd_description = """
  • -

    doxygen = false (or = true if master )

    +

    doxygen = false (or = true if main )

    • Build doxygen, docs will be uploaded to jenkins,

      @@ -246,7 +246,7 @@ cmd_description = """
  • -

    pushDockerTag = 'not-supposed-to-be-pushed'(or = master if master)

    +

    pushDockerTag = 'not-supposed-to-be-pushed'(or = main if main)

    • if packagePush=true it the name of docker tag that will be pushed

      @@ -257,7 +257,7 @@ cmd_description = """
  • -

    packagePush = false (or = true if master )

    +

    packagePush = false (or = true if main )

    • push all packages and docker to the artifactory and docker hub

      @@ -312,7 +312,7 @@ cmd_description = """
  • -

    specialBranch = false (or = true if master ),

    +

    specialBranch = false (or = true if main ),

    • Not recommended to set, it used to decide push doxygen and iroha:develop-build or not, and force to run build_type = 'Release'

      diff --git a/Jenkinsfile b/Jenkinsfile index 3ad5b97f118..8b1aa40b56f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -170,7 +170,7 @@ node ('master') { use_burrow = false forceDockerDevelopBuild = false - if (scmVars.GIT_LOCAL_BRANCH in ["master"] || env.TAG_NAME ) + if (scmVars.GIT_LOCAL_BRANCH in ["main"] || env.TAG_NAME ) specialBranch = true else specialBranch = false @@ -184,8 +184,8 @@ node ('master') { use_burrow = true } - if (scmVars.GIT_LOCAL_BRANCH == "master") - pushDockerTag = 'master' + if (scmVars.GIT_LOCAL_BRANCH == "main") + pushDockerTag = 'main' else if (env.TAG_NAME) pushDockerTag = env.TAG_NAME else @@ -346,7 +346,7 @@ node ('master') { x64LinuxAlwaysPostSteps, "x86_64 Linux ${build_type} ${compiler}", x64LinuxWorker, tasks) } } - // If "master" also run Release build + // If "main" branch also run Release build if (release_build){ registerBuildSteps([{x64LinuxBuildScript.buildSteps( current_parallelism, first_compiler, 'Release', build_shared_libs, specialBranch, /*coverage*/false, @@ -354,7 +354,7 @@ node ('master') { /*fuzzing*/false, /*benchmarking*/false, /*coredumps*/false, /*use_btf*/false, use_libursa, use_burrow, /*force_docker_develop_build*/false, /*manifest_push*/false, environmentList)}], x64LinuxPostSteps, "x86_64 Linux Release ${first_compiler}", x64LinuxWorker, tasks) - // will not be executed in usual case, because x64linux_compiler_list = ['gcc9'] for master branch or tags + // will not be executed in usual case, because x64linux_compiler_list = ['gcc9'] for main branch or tags if (x64linux_compiler_list.size() > 1){ x64linux_compiler_list[1..-1].each { compiler -> registerBuildSteps([{x64LinuxBuildScript.buildSteps( From 9701b253d507f8e7146e43b896d36ce441fe4d66 Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Wed, 10 Mar 2021 16:57:57 +0300 Subject: [PATCH 08/86] Feature/subscription engine (#813) * subscription: utils Signed-off-by: iceseer * threaded handler Signed-off-by: iceseer * threaded handler queue Signed-off-by: iceseer * threaded handler queue Signed-off-by: iceseer * threaded handler queue Signed-off-by: iceseer * refactoring Signed-off-by: iceseer * dispatcher Signed-off-by: iceseer * dispatcher Signed-off-by: iceseer * subscription engine Signed-off-by: iceseer * format Signed-off-by: iceseer * compile-time murmur2 Signed-off-by: iceseer * engine refactoring Signed-off-by: iceseer * dispatcher in engine Signed-off-by: iceseer * TID chained Signed-off-by: iceseer * dispatcher checks Signed-off-by: iceseer * engine multithreaded Signed-off-by: iceseer * format Signed-off-by: iceseer * format Signed-off-by: iceseer * manager Signed-off-by: iceseer * compiled Signed-off-by: iceseer * compiled Signed-off-by: iceseer * work Signed-off-by: iceseer * refactoring Signed-off-by: iceseer * format Signed-off-by: iceseer * comments Signed-off-by: iceseer * test Signed-off-by: iceseer * rollback test Signed-off-by: iceseer # Conflicts: # irohad/main/application.cpp * move args Signed-off-by: iceseer * thread safe Signed-off-by: iceseer * fixup! Signed-off-by: iceseer * refactoring Signed-off-by: iceseer * format Signed-off-by: iceseer * rw object holder Signed-off-by: iceseer * rename Signed-off-by: iceseer * SE sync call Signed-off-by: iceseer * issue fixes Signed-off-by: iceseer * revert sync call Signed-off-by: iceseer Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- irohad/subscription/common.hpp | 87 ++++++++++ irohad/subscription/dispatcher.hpp | 47 ++++++ irohad/subscription/subscriber.hpp | 52 ++++++ irohad/subscription/subscriber_impl.hpp | 166 +++++++++++++++++++ irohad/subscription/subscription_engine.hpp | 162 ++++++++++++++++++ irohad/subscription/subscription_manager.hpp | 102 ++++++++++++ irohad/subscription/thread_handler.hpp | 135 +++++++++++++++ libs/common/compile-time_murmur2.hpp | 101 +++++++++++ 8 files changed, 852 insertions(+) create mode 100644 irohad/subscription/common.hpp create mode 100644 irohad/subscription/dispatcher.hpp create mode 100644 irohad/subscription/subscriber.hpp create mode 100644 irohad/subscription/subscriber_impl.hpp create mode 100644 irohad/subscription/subscription_engine.hpp create mode 100644 irohad/subscription/subscription_manager.hpp create mode 100644 irohad/subscription/thread_handler.hpp create mode 100644 libs/common/compile-time_murmur2.hpp diff --git a/irohad/subscription/common.hpp b/irohad/subscription/common.hpp new file mode 100644 index 00000000000..9db2def6c7f --- /dev/null +++ b/irohad/subscription/common.hpp @@ -0,0 +1,87 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_COMMON_HPP +#define IROHA_SUBSCRIPTION_COMMON_HPP + +#include +#include +#include + +namespace iroha::utils { + + struct NoCopy { + NoCopy(NoCopy const &) = delete; + NoCopy &operator=(NoCopy const &) = delete; + NoCopy() = default; + }; + + struct NoMove { + NoMove(NoMove &&) = delete; + NoMove &operator=(NoMove &&) = delete; + NoMove() = default; + }; + + /** + * Protected object wrapper. Allow read-write access. + * @tparam T object type + * Example: + * @code + * ReadWriteObject obj("1"); + * bool const is_one_att1 = obj.sharedAccess([](auto const &str) { return str == "1"; }); + * obj.exclusiveAccess([](auto &str) { str = "2"; }); + * bool const is_one_att2 = obj.sharedAccess([](auto const &str) { return str == "1"; }); + * std::cout << + * "Attempt 1: " << is_one_att1 << std::endl << + * "Attempt 2: " << is_one_att2; + * @endcode + */ + template + struct ReadWriteObject { + template + ReadWriteObject(Args &&... args) : t_(std::forward(args)...) {} + + template + inline auto exclusiveAccess(F &&f) { + std::unique_lock lock(cs_); + return std::forward(f)(t_); + } + + template + inline auto sharedAccess(F &&f) const { + std::shared_lock lock(cs_); + return std::forward(f)(t_); + } + + private: + T t_; + mutable std::shared_mutex cs_; + }; + + class WaitForSingleObject final : NoMove, NoCopy { + std::condition_variable wait_cv_; + std::mutex wait_m_; + std::atomic_flag flag_; + + public: + WaitForSingleObject() { + flag_.test_and_set(); + } + + bool wait(std::chrono::microseconds wait_timeout) { + std::unique_lock _lock(wait_m_); + return wait_cv_.wait_for(_lock, + wait_timeout, + [&]() { return !flag_.test_and_set(); }); + } + + void set() { + flag_.clear(); + wait_cv_.notify_one(); + } + }; +} // namespace iroha::utils + +#endif // IROHA_SUBSCRIPTION_COMMON_HPP diff --git a/irohad/subscription/dispatcher.hpp b/irohad/subscription/dispatcher.hpp new file mode 100644 index 00000000000..bae6dfc87b1 --- /dev/null +++ b/irohad/subscription/dispatcher.hpp @@ -0,0 +1,47 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_DISPATCHER_HPP +#define IROHA_SUBSCRIPTION_DISPATCHER_HPP + +#include "subscription/common.hpp" +#include "subscription/thread_handler.hpp" + +namespace iroha::subscription { + + template + class Dispatcher final : utils::NoCopy, utils::NoMove { + public: + static constexpr uint32_t kHandlersCount = kCount; + using Task = ThreadHandler::Task; + using Tid = uint32_t; + + private: + ThreadHandler handlers_[kHandlersCount]; + + public: + Dispatcher() = default; + + template + static constexpr void checkTid() { + static_assert(kId < kHandlersCount, "Unexpected TID handler."); + } + + template + void add(Tid tid, F &&f) { + assert(tid < kHandlersCount); + handlers_[tid].add(std::forward(f)); + } + + template + void addDelayed(Tid tid, std::chrono::microseconds timeout, F &&f) { + assert(tid < kHandlersCount); + handlers_[tid].addDelayed(timeout, std::forward(f)); + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_DISPATCHER_HPP diff --git a/irohad/subscription/subscriber.hpp b/irohad/subscription/subscriber.hpp new file mode 100644 index 00000000000..546892d0090 --- /dev/null +++ b/irohad/subscription/subscriber.hpp @@ -0,0 +1,52 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SUBSCRIBER_HPP +#define IROHA_SUBSCRIPTION_SUBSCRIBER_HPP + +#include +#include +#include +#include + +#include "subscription/common.hpp" + +namespace iroha::subscription { + + using SubscriptionSetId = uint32_t; + + /** + * Base class that determines the subscriber. + * @tparam EventKey type of listening event + * @tparam Dispatcher thread dispatcher to execute tasks + * @tparam Arguments list of event arguments + */ + template + class Subscriber : public std::enable_shared_from_this< + Subscriber>, + utils::NoMove, + utils::NoCopy { + protected: + Subscriber() = default; + + public: + using EventType = EventKey; + + virtual ~Subscriber() {} + + /** + * Notification callback function + * @param set_id the id of the subscription set + * @param key notified event + * @param args event data + */ + virtual void on_notify(SubscriptionSetId set_id, + const EventType &key, + Arguments &&... args) = 0; + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SUBSCRIBER_HPP diff --git a/irohad/subscription/subscriber_impl.hpp b/irohad/subscription/subscriber_impl.hpp new file mode 100644 index 00000000000..c969af4a526 --- /dev/null +++ b/irohad/subscription/subscriber_impl.hpp @@ -0,0 +1,166 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SUBSCRIBER_IMPL_HPP +#define IROHA_SUBSCRIPTION_SUBSCRIBER_IMPL_HPP + +#include +#include +#include +#include + +#include "subscription/common.hpp" +#include "subscription/subscriber.hpp" +#include "subscription/subscription_engine.hpp" + +namespace iroha::subscription { + + /** + * Is a wrapper class, which provides subscription to events from + * SubscriptionEngine + * @tparam EventKey is a type of a particular subscription event (might be a + * key from an observed storage or a specific event type from an enumeration). + * @tparam Dispatcher thread dispatcher + * @tparam Receiver is a type of an object which is a part of Subscriber's + * internal state and can be accessed on every event notification. + * @tparam Arguments is a set of types of objects that are passed on every + * event notification. + */ + template + class SubscriberImpl final + : public Subscriber { + public: + using ReceiverType = Receiver; + using Hash = size_t; + using Parent = Subscriber; + + using SubscriptionEngineType = SubscriptionEngine< + typename Parent::EventType, + Dispatcher, + Subscriber>; + using SubscriptionEnginePtr = std::shared_ptr; + + using CallbackFnType = + std::function; + + private: + using SubscriptionsContainer = + std::unordered_map; + using SubscriptionsSets = + std::unordered_map; + + std::atomic next_id_; + SubscriptionEnginePtr engine_; + ReceiverType object_; + + std::mutex subscriptions_cs_; + SubscriptionsSets subscriptions_sets_; + + CallbackFnType on_notify_callback_; + + public: + template + SubscriberImpl(SubscriptionEnginePtr const &ptr, + SubscriberConstructorArgs &&... args) + : next_id_(0ull), + engine_(ptr), + object_(std::forward(args)...) {} + + ~SubscriberImpl() { + // Unsubscribe all + for (auto &[_, subscriptions] : subscriptions_sets_) + for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); + } + + void setCallback(CallbackFnType &&f) { + on_notify_callback_ = std::move(f); + } + + SubscriptionSetId generateSubscriptionSetId() { + return ++next_id_; + } + + template + void subscribe(SubscriptionSetId id, + const typename Parent::EventType &key) { + std::lock_guard lock(subscriptions_cs_); + auto &&[it, inserted] = subscriptions_sets_[id].emplace( + key, typename SubscriptionEngineType::IteratorType{}); + + /// Here we check first local subscriptions because of strong connection + /// with SubscriptionEngine. + if (inserted) + it->second = engine_->template subscribe( + id, key, Parent::weak_from_this()); + } + + /** + * @param id -- subscription set id that unsubscribes from \arg key + * @param key -- event key to unsubscribe from + * @return true if was subscribed to \arg key, false otherwise + */ + bool unsubscribe(SubscriptionSetId id, + const typename Parent::EventType &key) { + std::lock_guard lock(subscriptions_cs_); + if (auto set_it = subscriptions_sets_.find(id); + set_it != subscriptions_sets_.end()) { + auto &subscriptions = set_it->second; + auto it = subscriptions.find(key); + if (subscriptions.end() != it) { + engine_->unsubscribe(key, it->second); + subscriptions.erase(it); + return true; + } + } + return false; + } + + /** + * @param id -- subscription set id to unsubscribe from + * @return true if was subscribed to \arg id, false otherwise + */ + bool unsubscribe(SubscriptionSetId id) { + std::lock_guard lock(subscriptions_cs_); + if (auto set_it = subscriptions_sets_.find(id); + set_it != subscriptions_sets_.end()) { + auto &subscriptions = set_it->second; + for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); + + subscriptions_sets_.erase(set_it); + return true; + } + return false; + } + + void unsubscribe() { + std::lock_guard lock(subscriptions_cs_); + for (auto &[_, subscriptions] : subscriptions_sets_) + for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); + + subscriptions_sets_.clear(); + } + + void on_notify(SubscriptionSetId set_id, + const typename Parent::EventType &key, + Arguments &&... args) override { + if (nullptr != on_notify_callback_) + on_notify_callback_(set_id, object_, key, std::move(args)...); + } + + ReceiverType &get() { + return object_; + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SUBSCRIBER_IMPL_HPP diff --git a/irohad/subscription/subscription_engine.hpp b/irohad/subscription/subscription_engine.hpp new file mode 100644 index 00000000000..3a45ded6669 --- /dev/null +++ b/irohad/subscription/subscription_engine.hpp @@ -0,0 +1,162 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SUBSCRIPTION_ENGINE_HPP +#define IROHA_SUBSCRIPTION_SUBSCRIPTION_ENGINE_HPP + +#include +#include +#include +#include + +#include "subscription/common.hpp" +#include "subscription/dispatcher.hpp" +#include "subscription/subscriber.hpp" + +namespace iroha::subscription { + + /** + * @tparam EventKey - the type of a specific event from event set (e. g. a key + * from a storage or a particular kind of event from an enumeration) + * @tparam Dispatcher - thread handler + * @tparam Receiver - the type of an object that is a part of a Subscriber + * internal state and can be accessed on every event + */ + template + class SubscriptionEngine final + : public std::enable_shared_from_this< + SubscriptionEngine>, + utils::NoMove, + utils::NoCopy { + public: + using EventKeyType = EventKey; + using ReceiverType = Receiver; + using SubscriberType = Receiver; + using SubscriberWeakPtr = std::weak_ptr; + using DispatcherType = typename std::decay::type; + using DispatcherPtr = std::shared_ptr; + + /// List is preferable here because this container iterators remain + /// alive after removal from the middle of the container + /// using custom allocator + using SubscribersContainer = std::list>; + using IteratorType = typename SubscribersContainer::iterator; + + public: + explicit SubscriptionEngine(DispatcherPtr const &dispatcher) + : dispatcher_(dispatcher) { + assert(dispatcher_); + } + ~SubscriptionEngine() = default; + + private: + struct SubscriptionContext final { + std::mutex subscribers_list_cs; + SubscribersContainer subscribers_list; + }; + using KeyValueContainer = + std::unordered_map; + + mutable std::shared_mutex subscribers_map_cs_; + KeyValueContainer subscribers_map_; + DispatcherPtr dispatcher_; + + public: + template + IteratorType subscribe(SubscriptionSetId set_id, + const EventKeyType &key, + SubscriberWeakPtr ptr) { + Dispatcher::template checkTid(); + std::unique_lock lock(subscribers_map_cs_); + auto &subscribers_context = subscribers_map_[key]; + + std::lock_guard l(subscribers_context.subscribers_list_cs); + return subscribers_context.subscribers_list.emplace( + subscribers_context.subscribers_list.end(), + std::make_tuple(kTid, set_id, std::move(ptr))); + } + + void unsubscribe(const EventKeyType &key, const IteratorType &it_remove) { + std::unique_lock lock(subscribers_map_cs_); + auto it = subscribers_map_.find(key); + if (subscribers_map_.end() != it) { + auto &subscribers_context = it->second; + std::lock_guard l(subscribers_context.subscribers_list_cs); + subscribers_context.subscribers_list.erase(it_remove); + if (subscribers_context.subscribers_list.empty()) + subscribers_map_.erase(it); + } + } + + size_t size(const EventKeyType &key) const { + std::shared_lock lock(subscribers_map_cs_); + if (auto it = subscribers_map_.find(key); it != subscribers_map_.end()) { + auto &subscribers_context = it->second; + std::lock_guard l(subscribers_context.subscribers_list_cs); + return subscribers_context.subscribers_list.size(); + } + return 0ull; + } + + size_t size() const { + std::shared_lock lock(subscribers_map_cs_); + size_t count = 0ull; + for (auto &it : subscribers_map_) { + auto &subscribers_context = it->second; + std::lock_guard l(subscribers_context.subscribers_list_cs); + count += subscribers_context.subscribers_list.size(); + } + return count; + } + + template + void notify(const EventKeyType &key, EventParams const &... args) { + notifyDelayed(std::chrono::microseconds(0ull), key, args...); + } + + template + void notifyDelayed(std::chrono::microseconds timeout, + const EventKeyType &key, + EventParams const &... args) { + std::shared_lock lock(subscribers_map_cs_); + auto it = subscribers_map_.find(key); + if (subscribers_map_.end() == it) + return; + + auto &subscribers_container = it->second; + std::lock_guard l(subscribers_container.subscribers_list_cs); + for (auto it_sub = subscribers_container.subscribers_list.begin(); + it_sub != subscribers_container.subscribers_list.end();) { + auto wsub = std::get<2>(*it_sub); + auto id = std::get<1>(*it_sub); + + if (auto sub = wsub.lock()) { + dispatcher_->addDelayed(std::get<0>(*it_sub), + timeout, + [wsub(std::move(wsub)), + id(id), + key(key), + args = std::make_tuple(args...)]() mutable { + if (auto sub = wsub.lock()) + std::apply( + [&](auto &&... args) { + sub->on_notify( + id, key, std::move(args)...); + }, + std::move(args)); + }); + ++it_sub; + } else { + it_sub = subscribers_container.subscribers_list.erase(it_sub); + } + } + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SUBSCRIPTION_ENGINE_HPP diff --git a/irohad/subscription/subscription_manager.hpp b/irohad/subscription/subscription_manager.hpp new file mode 100644 index 00000000000..2e8a314975f --- /dev/null +++ b/irohad/subscription/subscription_manager.hpp @@ -0,0 +1,102 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SUBSCRIPTION_MANAGER_HPP +#define IROHA_SUBSCRIPTION_SUBSCRIPTION_MANAGER_HPP + +#include +#include +#include + +#include "common/compile-time_murmur2.hpp" +#include "subscription/common.hpp" +#include "subscription/dispatcher.hpp" +#include "subscription/subscriber.hpp" +#include "subscription/subscription_engine.hpp" + +namespace iroha::subscription { + + /** + * Class-aggregator that keeps all event engines inside. On notification it + * selects the appropriate engine and calls notification in it. + * @tparam kHandlersCount number of supported thread handlers + */ + template + class SubscriptionManager final : public std::enable_shared_from_this< + SubscriptionManager>, + utils::NoMove, + utils::NoCopy { + public: + using Dispatcher = subscription::Dispatcher; + + private: + using EngineHash = uint64_t; + using DispatcherPtr = std::shared_ptr; + using EnginesList = std::unordered_map>; + + private: + DispatcherPtr dispatcher_; + std::mutex engines_cs_; + EnginesList engines_; + + private: + template + static constexpr EngineHash getSubscriptionType() { + constexpr EngineHash value = CT_MURMUR2(__PRETTY_FUNCTION__); + return value; + } + + public: + SubscriptionManager() : dispatcher_(std::make_shared()) {} + + template + auto getEngine() { + using EngineType = + SubscriptionEngine>; + constexpr auto engineId = getSubscriptionType(); + std::lock_guard lock(engines_cs_); + if (auto it = engines_.find(engineId); it != engines_.end()) { + return std::reinterpret_pointer_cast(it->second); + } + auto obj = std::make_shared(dispatcher_); + engines_[engineId] = std::reinterpret_pointer_cast(obj); + return obj; + } + + template + void notify(const EventKey &key, Args const &... args) { + notifyDelayed(std::chrono::microseconds(0ull), key, args...); + } + + template + void notifyDelayed(std::chrono::microseconds timeout, + const EventKey &key, + Args const &... args) { + using EngineType = + SubscriptionEngine>; + constexpr auto engineId = getSubscriptionType(); + std::shared_ptr engine; + { + std::lock_guard lock(engines_cs_); + if (auto it = engines_.find(engineId); it != engines_.end()) + engine = std::reinterpret_pointer_cast(it->second); + else + return; + } + if (engine) + engine->notifyDelayed(timeout, key, args...); + } + + DispatcherPtr dispatcher() const { + return dispatcher_; + } + }; +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SUBSCRIPTION_MANAGER_HPP diff --git a/irohad/subscription/thread_handler.hpp b/irohad/subscription/thread_handler.hpp new file mode 100644 index 00000000000..4a95906584e --- /dev/null +++ b/irohad/subscription/thread_handler.hpp @@ -0,0 +1,135 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_THREAD_HANDLER_HPP +#define IROHA_SUBSCRIPTION_THREAD_HANDLER_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "subscription/common.hpp" + +namespace iroha::subscription { + + class ThreadHandler final : utils::NoCopy, utils::NoMove { + public: + using Task = std::function; + + private: + using Time = std::chrono::high_resolution_clock; + using Timepoint = std::chrono::time_point
  • -

    doxygen = false (or = true if master )

    +

    doxygen = false (or = true if main )

    • Build doxygen, docs will be uploaded to jenkins,

      @@ -246,7 +246,7 @@ cmd_description = """
  • -

    pushDockerTag = 'not-supposed-to-be-pushed'(or = master if master)

    +

    pushDockerTag = 'not-supposed-to-be-pushed'(or = main if main)

    • if packagePush=true it the name of docker tag that will be pushed

      @@ -257,7 +257,7 @@ cmd_description = """
  • -

    packagePush = false (or = true if master )

    +

    packagePush = false (or = true if main )

    • push all packages and docker to the artifactory and docker hub

      @@ -312,7 +312,7 @@ cmd_description = """
  • -

    specialBranch = false (or = true if master ),

    +

    specialBranch = false (or = true if main ),

    • Not recommended to set, it used to decide push doxygen and iroha:develop-build or not, and force to run build_type = 'Release'

      diff --git a/.packer/README.md b/.packer/README.md index 015a67e06cd..7d9dcd95fb1 100644 --- a/.packer/README.md +++ b/.packer/README.md @@ -1,7 +1,7 @@ ## Quick Start ``` cd win/ -packer build -var 'windows_password=' -var 'security_group_id=' -var 'iroha_repo=https://github.com/hyperledger/iroha.git' -var 'iroha_branches=master, support/1.1.x' windows-build-server.json +packer build -var 'windows_password=' -var 'security_group_id=' -var 'iroha_repo=https://github.com/hyperledger/iroha.git' -var 'iroha_branches=main, support/1.1.x' windows-build-server.json ``` Where : diff --git a/.packer/win/scripts/vcpkg_for_multiple_branch.ps1 b/.packer/win/scripts/vcpkg_for_multiple_branch.ps1 index c7121686e4a..3d4b11d25c6 100644 --- a/.packer/win/scripts/vcpkg_for_multiple_branch.ps1 +++ b/.packer/win/scripts/vcpkg_for_multiple_branch.ps1 @@ -1,10 +1,10 @@ # This script runs vcpkg.ps1 multiple time with different iroha branches # It is very helpful then you need setup multiple vcpkg on windows build agent, -# for example build master and develop from one ami +# for example build main and develop from one ami param( [string] $iroha_repo = "https://github.com/hyperledger/iroha.git", - [array] $branches = "master" + [array] $branches = "main" ) $ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue'; diff --git a/Jenkinsfile b/Jenkinsfile index 3ad5b97f118..501a9c27b82 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -170,7 +170,7 @@ node ('master') { use_burrow = false forceDockerDevelopBuild = false - if (scmVars.GIT_LOCAL_BRANCH in ["master"] || env.TAG_NAME ) + if (scmVars.GIT_LOCAL_BRANCH in ["main"] || env.TAG_NAME ) specialBranch = true else specialBranch = false @@ -184,8 +184,8 @@ node ('master') { use_burrow = true } - if (scmVars.GIT_LOCAL_BRANCH == "master") - pushDockerTag = 'master' + if (scmVars.GIT_LOCAL_BRANCH == "main") + pushDockerTag = 'main' else if (env.TAG_NAME) pushDockerTag = env.TAG_NAME else @@ -346,7 +346,7 @@ node ('master') { x64LinuxAlwaysPostSteps, "x86_64 Linux ${build_type} ${compiler}", x64LinuxWorker, tasks) } } - // If "master" also run Release build + // If "main" also run Release build if (release_build){ registerBuildSteps([{x64LinuxBuildScript.buildSteps( current_parallelism, first_compiler, 'Release', build_shared_libs, specialBranch, /*coverage*/false, @@ -354,7 +354,7 @@ node ('master') { /*fuzzing*/false, /*benchmarking*/false, /*coredumps*/false, /*use_btf*/false, use_libursa, use_burrow, /*force_docker_develop_build*/false, /*manifest_push*/false, environmentList)}], x64LinuxPostSteps, "x86_64 Linux Release ${first_compiler}", x64LinuxWorker, tasks) - // will not be executed in usual case, because x64linux_compiler_list = ['gcc9'] for master branch or tags + // will not be executed in usual case, because x64linux_compiler_list = ['gcc9'] for main branch or tags if (x64linux_compiler_list.size() > 1){ x64linux_compiler_list[1..-1].each { compiler -> registerBuildSteps([{x64LinuxBuildScript.buildSteps( @@ -420,7 +420,7 @@ node ('master') { s390xLinuxAlwaysPostSteps, "s390x Linux ${build_type} ${compiler}", s390xLinuxWorker, tasks) } } - // If "master" also run Release build + // If "main" also run Release build if (release_build){ registerBuildSteps([{x64LinuxBuildScript.buildSteps( current_parallelism, first_compiler, 'Release', build_shared_libs, specialBranch, /*coverage_s390x*/false, @@ -446,7 +446,7 @@ node ('master') { mac_compiler_list, build_type, coverage_mac, testing, testList, packageBuild, fuzzing, benchmarking, useBTF, use_libursa, use_burrow, environmentList)}], release_build ? x64MacAlwaysPostSteps : x64MacPostSteps, "Mac ${build_type}", x64MacWorker, tasks) - //If "master" also run Release build + //If "main" also run Release build if (release_build) { registerBuildSteps([{x64BuildScript.buildSteps(current_parallelism, mac_compiler_list, 'Release', /*coverage_mac*/false, /*testing*/false, testList, /*packageBuild*/true, /*fuzzing*/false, /*benchmarking*/false, /*use_btf*/false, From fbe3eddd9ff749046432cb6d0d96f05e2d8092f5 Mon Sep 17 00:00:00 2001 From: iceseer Date: Mon, 5 Apr 2021 15:35:56 +0300 Subject: [PATCH 16/86] build fix Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Signed-off-by: iceseer --- irohad/ametsuchi/impl/flat_file/flat_file.cpp | 3 +++ irohad/subscription/common.hpp | 4 ++-- irohad/subscription/thread_handler.hpp | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/irohad/ametsuchi/impl/flat_file/flat_file.cpp b/irohad/ametsuchi/impl/flat_file/flat_file.cpp index cbd89e2501e..eec03d971a3 100644 --- a/irohad/ametsuchi/impl/flat_file/flat_file.cpp +++ b/irohad/ametsuchi/impl/flat_file/flat_file.cpp @@ -20,8 +20,11 @@ #include "logger/logger.hpp" #ifdef _WIN32 +// We skip format here because of strong including order +// clang-format off #include #include +// clang-format on #endif using namespace iroha::ametsuchi; diff --git a/irohad/subscription/common.hpp b/irohad/subscription/common.hpp index 9bb959198c3..7f50bcd2009 100644 --- a/irohad/subscription/common.hpp +++ b/irohad/subscription/common.hpp @@ -10,7 +10,7 @@ #include #include -#if __clang__ +#ifdef _LIBCPP_VERSION namespace std { template @@ -20,7 +20,7 @@ namespace std { } } // namespace std -#endif +#endif //_LIBCPP_VERSION namespace iroha::utils { diff --git a/irohad/subscription/thread_handler.hpp b/irohad/subscription/thread_handler.hpp index 4a95906584e..3e6b5c8fd95 100644 --- a/irohad/subscription/thread_handler.hpp +++ b/irohad/subscription/thread_handler.hpp @@ -7,6 +7,7 @@ #define IROHA_SUBSCRIPTION_THREAD_HANDLER_HPP #include +#include #include #include #include @@ -81,7 +82,7 @@ namespace iroha::subscription { auto const &first = tasks_.front(); if (first.timepoint > before) { return std::chrono::duration_cast( - first.timepoint - before); + first.timepoint - before); } return std::chrono::microseconds(0ull); } From e90a584165f00010aeb6c60585e5ea280edb54ff Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Wed, 17 Mar 2021 15:34:54 +0100 Subject: [PATCH 17/86] OnDemandOrderingServiceImpl: remove boost::adaptors::filter usage Signed-off-by: Andrei Lebedev --- .../impl/on_demand_ordering_service_impl.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/irohad/ordering/impl/on_demand_ordering_service_impl.cpp b/irohad/ordering/impl/on_demand_ordering_service_impl.cpp index a22f5a1f2e8..ff4f2017e5c 100644 --- a/irohad/ordering/impl/on_demand_ordering_service_impl.cpp +++ b/irohad/ordering/impl/on_demand_ordering_service_impl.cpp @@ -8,10 +8,7 @@ #include #include -#include #include -#include -#include #include #include "ametsuchi/tx_presence_cache.hpp" #include "ametsuchi/tx_presence_cache_utils.hpp" @@ -54,15 +51,11 @@ void OnDemandOrderingServiceImpl::onCollaborationOutcome( // ----------------------------| OdOsNotification |----------------------------- void OnDemandOrderingServiceImpl::onBatches(CollectionType batches) { - auto unprocessed_batches = - boost::adaptors::filter(batches, [this](const auto &batch) { - log_->debug("check batch {} for already processed transactions", - batch->reducedHash().hex()); - return not this->batchAlreadyProcessed(*batch); - }); - std::for_each(unprocessed_batches.begin(), - unprocessed_batches.end(), - [this](auto &obj) { insertBatchToCache(obj); }); + for (auto &batch : batches) { + if (not batchAlreadyProcessed(*batch)) { + insertBatchToCache(batch); + } + } log_->info("onBatches => collection size = {}", batches.size()); } From 819c45784b7947a6138b4cd8f2d4bb41b69bc6bf Mon Sep 17 00:00:00 2001 From: iceseer Date: Wed, 7 Apr 2021 12:54:08 +0300 Subject: [PATCH 18/86] SE features update Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Signed-off-by: iceseer --- irohad/main/impl/subscription.cpp | 17 +- irohad/main/subscription.hpp | 47 +- irohad/subscription/common.hpp | 46 +- irohad/subscription/dispatcher.hpp | 72 +- irohad/subscription/subscriber.hpp | 6 +- irohad/subscription/subscriber_impl.hpp | 56 +- irohad/subscription/subscription_engine.hpp | 88 ++- irohad/subscription/subscription_manager.hpp | 98 ++- irohad/subscription/thread_handler.hpp | 53 +- libs/common/compile-time_murmur2.hpp | 11 +- test/module/irohad/CMakeLists.txt | 1 + .../module/irohad/subscription/CMakeLists.txt | 9 + .../subscription/subscription_mocks.hpp | 59 ++ .../irohad/subscription/subscription_test.cpp | 654 ++++++++++++++++++ 14 files changed, 1124 insertions(+), 93 deletions(-) create mode 100644 test/module/irohad/subscription/CMakeLists.txt create mode 100644 test/module/irohad/subscription/subscription_mocks.hpp create mode 100644 test/module/irohad/subscription/subscription_test.cpp diff --git a/irohad/main/impl/subscription.cpp b/irohad/main/impl/subscription.cpp index d6f12adb5bc..a46f708cab0 100644 --- a/irohad/main/impl/subscription.cpp +++ b/irohad/main/impl/subscription.cpp @@ -5,14 +5,23 @@ #include "main/subscription.hpp" -#include +#include namespace iroha { std::shared_ptr getSubscription() { - static std::shared_ptr engine = - std::make_shared(); - return engine; + static std::weak_ptr engine; + if (auto ptr = engine.lock()) + return ptr; + + static std::mutex engine_cs; + std::lock_guard lock(engine_cs); + if (auto ptr = engine.lock()) + return ptr; + + auto ptr = std::make_shared(); + engine = ptr; + return ptr; } } // namespace iroha diff --git a/irohad/main/subscription.hpp b/irohad/main/subscription.hpp index be856c44d2f..8ee1a0e4c76 100644 --- a/irohad/main/subscription.hpp +++ b/irohad/main/subscription.hpp @@ -15,6 +15,8 @@ namespace iroha { enum SubscriptionEngineHandlers { kYac = 0, + kRequestProposal, + kVoteProcess, //--------------- kTotalCount }; @@ -22,6 +24,7 @@ namespace iroha { enum EventTypes { kOnOutcome = 0, kOnSynchronization, + kOnInitialSynchronization, kOnCurrentRoundPeers, kOnRoundSwitch, kOnProposal, @@ -30,21 +33,59 @@ namespace iroha { kOnOutcomeFromYac, kOnOutcomeDelayed, kOnBlock, + kOnInitialBlock, kOnBlockCreatorEvent, kOnFinalizedTxs, kOnApplyState, + kOnNeedProposal, + kOnNewProposal, // MST kOnStateUpdate, kOnPreparedBatches, - kOnExpiredBatches + kOnExpiredBatches, + + // YAC + kTimer, + + // TEST + kOnTestOperationComplete }; - using Subscription = subscription::SubscriptionManager< - SubscriptionEngineHandlers::kTotalCount>; + static constexpr uint32_t kThreadPoolSize = 3u; + + using Subscription = + subscription::SubscriptionManager; using SubscriptionDispatcher = typename Subscription::Dispatcher; + template + using BaseSubscriber = subscription::SubscriberImpl; + std::shared_ptr getSubscription(); + + template + struct SubscriberCreator { + template + static auto create(F &&callback) { + auto subscriber = + std::make_shared>( + getSubscription()->getEngine()); + subscriber->setCallback( + [f{std::forward(callback)}](auto /*set_id*/, + auto &object, + auto event_key, + EventData... args) { + assert(key == event_key); + std::forward(f)(object, std::move(args)...); + }); + subscriber->template subscribe(0, key); + return subscriber; + } + }; } // namespace iroha #endif // IROHA_SUBSCRIPTION_HPP diff --git a/irohad/subscription/common.hpp b/irohad/subscription/common.hpp index 7f50bcd2009..738c87804a3 100644 --- a/irohad/subscription/common.hpp +++ b/irohad/subscription/common.hpp @@ -36,20 +36,31 @@ namespace iroha::utils { NoMove() = default; }; + // clang-format off /** * Protected object wrapper. Allow read-write access. * @tparam T object type * Example: * @code - * ReadWriteObject obj("1"); - * bool const is_one_att1 = obj.sharedAccess([](auto const &str) { return str == "1"; }); - * obj.exclusiveAccess([](auto &str) { str = "2"; }); - * bool const is_one_att2 = obj.sharedAccess([](auto const &str) { return str == "1"; }); + * ReadWriteObject obj("1"); + * bool const is_one_att1 = + * obj.sharedAccess([](auto const &str) { + * return str == "1"; + * }); + * obj.exclusiveAccess([](auto &str) { + * str = "2"; + * }); + * bool const is_one_att2 = + * obj.sharedAccess([](auto const &str) { + * return str == "1"; + * }); + * * std::cout << * "Attempt 1: " << is_one_att1 << std::endl << * "Attempt 2: " << is_one_att2; * @endcode */ + // clang-format on template struct ReadWriteObject { template @@ -75,21 +86,34 @@ namespace iroha::utils { class WaitForSingleObject final : NoMove, NoCopy { std::condition_variable wait_cv_; std::mutex wait_m_; - std::atomic_flag flag_; + bool flag_; public: - WaitForSingleObject() { - flag_.test_and_set(); - } + WaitForSingleObject() : flag_{true} {} bool wait(std::chrono::microseconds wait_timeout) { std::unique_lock _lock(wait_m_); - return wait_cv_.wait_for( - _lock, wait_timeout, [&]() { return !flag_.test_and_set(); }); + return wait_cv_.wait_for(_lock, wait_timeout, [&]() { + auto prev = !flag_; + flag_ = true; + return prev; + }); + } + + void wait() { + std::unique_lock _lock(wait_m_); + wait_cv_.wait(_lock, [&]() { + auto prev = !flag_; + flag_ = true; + return prev; + }); } void set() { - flag_.clear(); + { + std::unique_lock _lock(wait_m_); + flag_ = false; + } wait_cv_.notify_one(); } }; diff --git a/irohad/subscription/dispatcher.hpp b/irohad/subscription/dispatcher.hpp index bae6dfc87b1..aa4aa33b49a 100644 --- a/irohad/subscription/dispatcher.hpp +++ b/irohad/subscription/dispatcher.hpp @@ -11,34 +11,90 @@ namespace iroha::subscription { - template + template class Dispatcher final : utils::NoCopy, utils::NoMove { public: static constexpr uint32_t kHandlersCount = kCount; + static constexpr uint32_t kPoolThreadsCount = kPoolSize; using Task = ThreadHandler::Task; using Tid = uint32_t; + static constexpr Tid kExecuteInPool = std::numeric_limits::max(); private: - ThreadHandler handlers_[kHandlersCount]; + struct ThreadHandlerContext { + /// Worker thread to execute tasks + std::shared_ptr handler; + + /// Shows if this handler is static or if it was created to + /// execute a single task and should be deleted after performing it + bool is_temporary; + }; + + ThreadHandlerContext handlers_[kHandlersCount]; + ThreadHandlerContext pool_[kPoolThreadsCount]; + + inline ThreadHandlerContext findHandler(Tid const tid) { + assert(tid < kHandlersCount || tid == kExecuteInPool); + if (tid < kHandlersCount) + return handlers_[tid]; + + for (auto &handler : pool_) + if (!handler.handler->isBusy()) + return handler; + + return ThreadHandlerContext{ + std::make_shared(), + true // temporary + }; + } public: - Dispatcher() = default; + Dispatcher() { + for (auto &h : handlers_) { + h.handler = std::make_shared(); + h.is_temporary = false; + } + for (auto &h : pool_) { + h.handler = std::make_shared(); + h.is_temporary = false; + } + } + + void dispose() { + for (auto &h : handlers_) h.handler->dispose(); + for (auto &h : pool_) h.handler->dispose(); + } template static constexpr void checkTid() { - static_assert(kId < kHandlersCount, "Unexpected TID handler."); + static_assert(kId < kHandlersCount || kId == kExecuteInPool, + "Unexpected TID handler."); } template void add(Tid tid, F &&f) { - assert(tid < kHandlersCount); - handlers_[tid].add(std::forward(f)); + auto h = findHandler(tid); + if (!h.is_temporary) + h.handler->add(std::forward(f)); + else { + h.handler->add([h, f{std::forward(f)}]() mutable { + std::forward(f)(); + h.handler->dispose(false); + }); + } } template void addDelayed(Tid tid, std::chrono::microseconds timeout, F &&f) { - assert(tid < kHandlersCount); - handlers_[tid].addDelayed(timeout, std::forward(f)); + auto h = findHandler(tid); + if (!h.is_temporary) + h.handler->addDelayed(timeout, std::forward(f)); + else { + h.handler->addDelayed(timeout, [h, f{std::forward(f)}]() mutable { + std::forward(f)(); + h.handler->dispose(false); + }); + } } }; diff --git a/irohad/subscription/subscriber.hpp b/irohad/subscription/subscriber.hpp index 546892d0090..815198f26e6 100644 --- a/irohad/subscription/subscriber.hpp +++ b/irohad/subscription/subscriber.hpp @@ -28,13 +28,9 @@ namespace iroha::subscription { Subscriber>, utils::NoMove, utils::NoCopy { - protected: - Subscriber() = default; - public: using EventType = EventKey; - - virtual ~Subscriber() {} + virtual ~Subscriber() = default; /** * Notification callback function diff --git a/irohad/subscription/subscriber_impl.hpp b/irohad/subscription/subscriber_impl.hpp index c969af4a526..0e236fc3430 100644 --- a/irohad/subscription/subscriber_impl.hpp +++ b/irohad/subscription/subscriber_impl.hpp @@ -44,12 +44,13 @@ namespace iroha::subscription { Dispatcher, Subscriber>; using SubscriptionEnginePtr = std::shared_ptr; + using SubscriptionEngineWPtr = std::weak_ptr; using CallbackFnType = std::function; + Arguments &&...)>; private: using SubscriptionsContainer = @@ -59,12 +60,21 @@ namespace iroha::subscription { std::unordered_map; std::atomic next_id_; - SubscriptionEnginePtr engine_; + + /// Subscription engine weak pointer + SubscriptionEngineWPtr engine_; + + /// Internal object stored in subscriber and available in notification + /// call ReceiverType object_; std::mutex subscriptions_cs_; + + /// Associative container with all active subscriptions: + /// subscription set_id -> notification event -> subscription iterator SubscriptionsSets subscriptions_sets_; + /// Stored notification callback CallbackFnType on_notify_callback_; public: @@ -75,11 +85,7 @@ namespace iroha::subscription { engine_(ptr), object_(std::forward(args)...) {} - ~SubscriberImpl() { - // Unsubscribe all - for (auto &[_, subscriptions] : subscriptions_sets_) - for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); - } + ~SubscriberImpl() {} void setCallback(CallbackFnType &&f) { on_notify_callback_ = std::move(f); @@ -89,18 +95,20 @@ namespace iroha::subscription { return ++next_id_; } - template + template void subscribe(SubscriptionSetId id, const typename Parent::EventType &key) { - std::lock_guard lock(subscriptions_cs_); - auto &&[it, inserted] = subscriptions_sets_[id].emplace( - key, typename SubscriptionEngineType::IteratorType{}); - - /// Here we check first local subscriptions because of strong connection - /// with SubscriptionEngine. - if (inserted) - it->second = engine_->template subscribe( - id, key, Parent::weak_from_this()); + if (auto engine = engine_.lock()) { + std::lock_guard lock(subscriptions_cs_); + auto &&[it, inserted] = subscriptions_sets_[id].emplace( + key, typename SubscriptionEngineType::IteratorType{}); + + /// Here we check first local subscriptions because of strong connection + /// with SubscriptionEngine. + if (inserted) + it->second = engine->template subscribe( + id, key, Parent::weak_from_this()); + } } /** @@ -116,7 +124,8 @@ namespace iroha::subscription { auto &subscriptions = set_it->second; auto it = subscriptions.find(key); if (subscriptions.end() != it) { - engine_->unsubscribe(key, it->second); + if (auto engine = engine_.lock()) + engine->unsubscribe(key, it->second); subscriptions.erase(it); return true; } @@ -132,8 +141,10 @@ namespace iroha::subscription { std::lock_guard lock(subscriptions_cs_); if (auto set_it = subscriptions_sets_.find(id); set_it != subscriptions_sets_.end()) { - auto &subscriptions = set_it->second; - for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); + if (auto engine = engine_.lock()) { + auto &subscriptions = set_it->second; + for (auto &[key, it] : subscriptions) engine->unsubscribe(key, it); + } subscriptions_sets_.erase(set_it); return true; @@ -143,8 +154,9 @@ namespace iroha::subscription { void unsubscribe() { std::lock_guard lock(subscriptions_cs_); - for (auto &[_, subscriptions] : subscriptions_sets_) - for (auto &[key, it] : subscriptions) engine_->unsubscribe(key, it); + if (auto engine = engine_.lock()) + for (auto &[_, subscriptions] : subscriptions_sets_) + for (auto &[key, it] : subscriptions) engine->unsubscribe(key, it); subscriptions_sets_.clear(); } diff --git a/irohad/subscription/subscription_engine.hpp b/irohad/subscription/subscription_engine.hpp index 3a45ded6669..16d90303fec 100644 --- a/irohad/subscription/subscription_engine.hpp +++ b/irohad/subscription/subscription_engine.hpp @@ -17,6 +17,10 @@ namespace iroha::subscription { + struct IDisposable { + virtual void dispose() = 0; + }; + /** * @tparam EventKey - the type of a specific event from event set (e. g. a key * from a storage or a particular kind of event from an enumeration) @@ -26,7 +30,8 @@ namespace iroha::subscription { */ template class SubscriptionEngine final - : public std::enable_shared_from_this< + : public IDisposable, + public std::enable_shared_from_this< SubscriptionEngine>, utils::NoMove, utils::NoCopy { @@ -53,7 +58,12 @@ namespace iroha::subscription { } ~SubscriptionEngine() = default; + void dispose() override { + dispatcher_.reset(); + } + private: + /// List of subscribers for a single event key struct SubscriptionContext final { std::mutex subscribers_list_cs; SubscribersContainer subscribers_list; @@ -62,10 +72,25 @@ namespace iroha::subscription { std::unordered_map; mutable std::shared_mutex subscribers_map_cs_; + + /// Associative container with lists of subscribers by the event key KeyValueContainer subscribers_map_; + + /// Thread handlers dispatcher DispatcherPtr dispatcher_; public: + /** + * Stores Subscriber object to retrieve later notifications + * @tparam kTid Thread ID in which subscribers callback will be executed + * @param set_id subscription set id is a group identifier in multiple + * subscriptions + * @param key notification event key that this subscriber will listen to + * @param ptr subscriber weak pointer + * @return a position in an internal container with subscribers(!!! it must + * be kept valid in case the other subscriber will be deleted from this + * container) + */ template IteratorType subscribe(SubscriptionSetId set_id, const EventKeyType &key, @@ -80,6 +105,11 @@ namespace iroha::subscription { std::make_tuple(kTid, set_id, std::move(ptr))); } + /** + * Stops the subscriber from listening to events + * @param key notification event that must be unsubscribed + * @param it_remove iterator to the subscribers position + */ void unsubscribe(const EventKeyType &key, const IteratorType &it_remove) { std::unique_lock lock(subscribers_map_cs_); auto it = subscribers_map_.find(key); @@ -92,6 +122,11 @@ namespace iroha::subscription { } } + /** + * Number of subscribers which listen to the current notification event + * @param key notification event + * @return number of subscribers + */ size_t size(const EventKeyType &key) const { std::shared_lock lock(subscribers_map_cs_); if (auto it = subscribers_map_.find(key); it != subscribers_map_.end()) { @@ -102,6 +137,10 @@ namespace iroha::subscription { return 0ull; } + /** + * Number of subscribers which listen to all notification events + * @return number of subscribers + */ size_t size() const { std::shared_lock lock(subscribers_map_cs_); size_t count = 0ull; @@ -113,15 +152,32 @@ namespace iroha::subscription { return count; } + /** + * Notify the event subscribers without delay + * @tparam EventParams notification event type + * @param key notification event to be executed + * @param args event data to transmit + */ template void notify(const EventKeyType &key, EventParams const &... args) { notifyDelayed(std::chrono::microseconds(0ull), key, args...); } + /** + * Notify the event subscribers after a specified delay + * @tparam EventParams notification event type + * @param timeout delay before subscribers will be notified + * @param key notification event to be executed + * @param args event data to transmit + */ template void notifyDelayed(std::chrono::microseconds timeout, const EventKeyType &key, EventParams const &... args) { + auto dispatcher = dispatcher_; + if (!dispatcher) + return; + std::shared_lock lock(subscribers_map_cs_); auto it = subscribers_map_.find(key); if (subscribers_map_.end() == it) @@ -134,21 +190,21 @@ namespace iroha::subscription { auto wsub = std::get<2>(*it_sub); auto id = std::get<1>(*it_sub); - if (auto sub = wsub.lock()) { - dispatcher_->addDelayed(std::get<0>(*it_sub), - timeout, - [wsub(std::move(wsub)), - id(id), - key(key), - args = std::make_tuple(args...)]() mutable { - if (auto sub = wsub.lock()) - std::apply( - [&](auto &&... args) { - sub->on_notify( - id, key, std::move(args)...); - }, - std::move(args)); - }); + if (!wsub.expired()) { + dispatcher->addDelayed(std::get<0>(*it_sub), + timeout, + [wsub(std::move(wsub)), + id(id), + key(key), + args = std::make_tuple(args...)]() mutable { + if (auto sub = wsub.lock()) + std::apply( + [&](auto &&... args) { + sub->on_notify( + id, key, std::move(args)...); + }, + std::move(args)); + }); ++it_sub; } else { it_sub = subscribers_container.subscribers_list.erase(it_sub); diff --git a/irohad/subscription/subscription_manager.hpp b/irohad/subscription/subscription_manager.hpp index 2e8a314975f..2e81b5bd0ab 100644 --- a/irohad/subscription/subscription_manager.hpp +++ b/irohad/subscription/subscription_manager.hpp @@ -22,14 +22,16 @@ namespace iroha::subscription { * Class-aggregator that keeps all event engines inside. On notification it * selects the appropriate engine and calls notification in it. * @tparam kHandlersCount number of supported thread handlers + * @tparam kPoolSize number of threads in thread pool */ - template - class SubscriptionManager final : public std::enable_shared_from_this< - SubscriptionManager>, - utils::NoMove, - utils::NoCopy { + template + class SubscriptionManager final + : public std::enable_shared_from_this< + SubscriptionManager>, + utils::NoMove, + utils::NoCopy { public: - using Dispatcher = subscription::Dispatcher; + using Dispatcher = subscription::Dispatcher; private: using EngineHash = uint64_t; @@ -37,41 +39,103 @@ namespace iroha::subscription { using EnginesList = std::unordered_map>; private: + /// Thread handlers dispatcher DispatcherPtr dispatcher_; - std::mutex engines_cs_; + std::shared_mutex engines_cs_; + /// Engines container EnginesList engines_; + std::atomic_flag disposed_; private: template - static constexpr EngineHash getSubscriptionType() { + static constexpr EngineHash getSubscriptionHash() { +#ifdef _WIN32 + constexpr EngineHash value = CT_MURMUR2(__FUNCSIG__); +#else //_WIN32 constexpr EngineHash value = CT_MURMUR2(__PRETTY_FUNCTION__); +#endif //_WIN32 return value; } public: - SubscriptionManager() : dispatcher_(std::make_shared()) {} + SubscriptionManager() : dispatcher_(std::make_shared()) { + disposed_.clear(); + } + + /** + * Detaches the dispatcher from all engines and stops thread handlers + * execution. + */ + void dispose() { + if (!disposed_.test_and_set()) { + { + std::shared_lock lock(engines_cs_); + for (auto &descriptor : engines_) + std::reinterpret_pointer_cast(descriptor.second) + ->dispose(); + } + dispatcher_->dispose(); + } + } + /** + * Method returns the engine corresponding to current arguments set + * transmission. + * @tparam EventKey typeof event enum + * @tparam Args arguments list of transmitted event data types + * @return engine object + */ template auto getEngine() { using EngineType = SubscriptionEngine>; - constexpr auto engineId = getSubscriptionType(); - std::lock_guard lock(engines_cs_); + constexpr auto engineId = getSubscriptionHash(); + { + std::shared_lock lock(engines_cs_); + if (auto it = engines_.find(engineId); it != engines_.end()) { + return std::reinterpret_pointer_cast(it->second); + } + } + std::unique_lock lock(engines_cs_); if (auto it = engines_.find(engineId); it != engines_.end()) { return std::reinterpret_pointer_cast(it->second); } + + /// To be sure IDisposable is the first base class, because of later cast + static_assert(std::is_base_of_v, + "Engine type must be derived from IDisposable."); + assert(uintptr_t(reinterpret_cast(0x1)) + == uintptr_t(static_cast( + reinterpret_cast(0x1)))); + auto obj = std::make_shared(dispatcher_); engines_[engineId] = std::reinterpret_pointer_cast(obj); return obj; } + /** + * Make event notification to subscribers that are listening to this event + * @tparam EventKey typeof event enum + * @tparam Args arguments list of transmitted event data types + * @param key event key + * @param args transmitted data + */ template void notify(const EventKey &key, Args const &... args) { notifyDelayed(std::chrono::microseconds(0ull), key, args...); } + /** + * Make event notification to subscribers that are listening this event + * after a delay + * @tparam EventKey typeof event enum + * @tparam Args arguments list of transmitted event data types + * @param timeout delay before subscribers will be notified + * @param key event key + * @param args transmitted data + */ template void notifyDelayed(std::chrono::microseconds timeout, const EventKey &key, @@ -80,19 +144,23 @@ namespace iroha::subscription { SubscriptionEngine>; - constexpr auto engineId = getSubscriptionType(); + constexpr auto engineId = getSubscriptionHash(); std::shared_ptr engine; { - std::lock_guard lock(engines_cs_); + std::shared_lock lock(engines_cs_); if (auto it = engines_.find(engineId); it != engines_.end()) engine = std::reinterpret_pointer_cast(it->second); else return; } - if (engine) - engine->notifyDelayed(timeout, key, args...); + assert(engine); + engine->notifyDelayed(timeout, key, args...); } + /** + * Getter to retrieve a dispatcher. + * @return dispatcher object + */ DispatcherPtr dispatcher() const { return dispatcher_; } diff --git a/irohad/subscription/thread_handler.hpp b/irohad/subscription/thread_handler.hpp index 3e6b5c8fd95..d517d317bb3 100644 --- a/irohad/subscription/thread_handler.hpp +++ b/irohad/subscription/thread_handler.hpp @@ -17,6 +17,12 @@ #include "subscription/common.hpp" +/** + * If you need to execute task, that was made in this thread and want to be + * executed in the same thread without delay - you need to uncomment this define + */ +//#define SE_SYNC_CALL_IF_SAME_THREAD + namespace iroha::subscription { class ThreadHandler final : utils::NoCopy, utils::NoMove { @@ -32,14 +38,27 @@ namespace iroha::subscription { }; using TaskContainer = std::deque; + /// Flag shows if thread loop should continue processing or exit std::atomic_flag proceed_; + mutable std::mutex tasks_cs_; + + /// List of tasks to be performed TaskContainer tasks_; + std::thread worker_; + + /// Event that is set when loop should make some work or exit utils::WaitForSingleObject event_; + /// Flag that shows if current handler is in task execution state + std::atomic is_busy_; + + std::thread::id id_; + private: inline void checkLocked() { + /// Need to check that we are locked in debug. assert(!tasks_cs_.try_lock()); } @@ -90,15 +109,18 @@ namespace iroha::subscription { } uint32_t process() { + id_ = std::this_thread::get_id(); Task task; do { while (extractExpired(task, now())) { + is_busy_.store(true, std::memory_order_relaxed); try { if (task) task(); } catch (...) { } } + is_busy_.store(false, std::memory_order_relaxed); event_.wait(untilFirst()); } while (proceed_.test_and_set()); return 0; @@ -107,14 +129,24 @@ namespace iroha::subscription { public: ThreadHandler() { proceed_.test_and_set(); + is_busy_.store(false); worker_ = std::thread( [](ThreadHandler *__this) { return __this->process(); }, this); } - ~ThreadHandler() { + ~ThreadHandler() {} + + void dispose(bool wait_for_release = true) { proceed_.clear(); event_.set(); - worker_.join(); + if (wait_for_release) + worker_.join(); + else + worker_.detach(); + } + + bool isBusy() const { + return is_busy_.load(); } template @@ -124,10 +156,19 @@ namespace iroha::subscription { template void addDelayed(std::chrono::microseconds timeout, F &&f) { - auto const tp = now() + timeout; - std::lock_guard lock(tasks_cs_); - insert(after(tp), TimedTask{tp, std::forward(f)}); - event_.set(); +#ifdef SE_SYNC_CALL_IF_SAME_THREAD + if (timeout == std::chrono::microseconds(0ull) + && id_ == std::this_thread::get_id()) + std::forward(f)(); + else { +#endif // SE_SYNC_CALL_IF_SAME_THREAD + auto const tp = now() + timeout; + std::lock_guard lock(tasks_cs_); + insert(after(tp), TimedTask{tp, std::forward(f)}); + event_.set(); +#ifdef SE_SYNC_CALL_IF_SAME_THREAD + } +#endif // SE_SYNC_CALL_IF_SAME_THREAD } }; diff --git a/libs/common/compile-time_murmur2.hpp b/libs/common/compile-time_murmur2.hpp index 9ea1d260976..394326308ca 100644 --- a/libs/common/compile-time_murmur2.hpp +++ b/libs/common/compile-time_murmur2.hpp @@ -94,8 +94,13 @@ namespace iroha::ct_hash { ::iroha::ct_hash::Hasher::murmur2(x, (sizeof(x) / sizeof(x[0])) - 1) #endif // CT_MURMUR2 -static_assert(CT_MURMUR2("Called the One Ring, or the Ruling Ring.") == 1333588607); -static_assert(CT_MURMUR2("Fashioned by Sauron a decade after the making of the Elven rings in the fires of Mount Doom in Mordor and which") == 1319897327); -static_assert(CT_MURMUR2("could only be destroyed in that same fire.") == 702138758); +static_assert(CT_MURMUR2("Called the One Ring, or the Ruling Ring.") + == 1333588607); +static_assert( + CT_MURMUR2("Fashioned by Sauron a decade after the making of the Elven " + "rings in the fires of Mount Doom in Mordor and which") + == 1319897327); +static_assert(CT_MURMUR2("could only be destroyed in that same fire.") + == 702138758); #endif // IROHA_MURMUR_2_HPP diff --git a/test/module/irohad/CMakeLists.txt b/test/module/irohad/CMakeLists.txt index 69315d8101e..f74ee5187e6 100644 --- a/test/module/irohad/CMakeLists.txt +++ b/test/module/irohad/CMakeLists.txt @@ -18,3 +18,4 @@ add_subdirectory(synchronizer) add_subdirectory(torii) add_subdirectory(validation) add_subdirectory(pending_txs_storage) +add_subdirectory(subscription) diff --git a/test/module/irohad/subscription/CMakeLists.txt b/test/module/irohad/subscription/CMakeLists.txt new file mode 100644 index 00000000000..c0f50602f59 --- /dev/null +++ b/test/module/irohad/subscription/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +addtest(subscription_test subscription_test.cpp) +target_link_libraries(subscription_test + subscription + ) diff --git a/test/module/irohad/subscription/subscription_mocks.hpp b/test/module/irohad/subscription/subscription_mocks.hpp new file mode 100644 index 00000000000..14a1cd46c79 --- /dev/null +++ b/test/module/irohad/subscription/subscription_mocks.hpp @@ -0,0 +1,59 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_MOCKS_HPP +#define IROHA_SUBSCRIPTION_MOCKS_HPP + +#include +#include "main/subscription.hpp" + +namespace iroha::subscription { + + template + class MockSubscriber : public Subscriber { + public: + using Parent = Subscriber; + using SubscriptionEngineType = SubscriptionEngine< + typename Parent::EventType, + Dispatcher, + Subscriber>; + using SubscriptionEnginePtr = std::shared_ptr; + + SubscriptionEnginePtr engine_; + MockSubscriber(SubscriptionEnginePtr const &engine) : engine_(engine) {} + + template + void subscribe(const typename Parent::EventType &key) { + engine_->template subscribe(0ull, key, Parent::weak_from_this()); + } + + MOCK_METHOD3_T(on_notify, + void(SubscriptionSetId, const EventKey &, Argument &&)); + }; + + class MockDispatcher { + public: + using Tid = uint32_t; + + public: + MockDispatcher() = default; + + template + static constexpr void checkTid() {} + + template + void add(Tid, F &&f) { + std::forward(f)(); + } + + template + void addDelayed(Tid, std::chrono::microseconds, F &&f) { + std::forward(f)(); + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_MOCKS_HPP diff --git a/test/module/irohad/subscription/subscription_test.cpp b/test/module/irohad/subscription/subscription_test.cpp new file mode 100644 index 00000000000..7b83ef54cc8 --- /dev/null +++ b/test/module/irohad/subscription/subscription_test.cpp @@ -0,0 +1,654 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "main/subscription.hpp" + +#include +#include + +#include "module/irohad/subscription/subscription_mocks.hpp" + +using namespace iroha; + +class SubscriptionTest : public ::testing::Test { + public: + template + auto createSubscriptionManager() { + using Manager = subscription::SubscriptionManager; + return std::make_shared(); + } + + template + auto createSubscriber(std::shared_ptr const &manager, + ObjectType &&initial, + F &&f) { + using Dispatcher = typename Manager::Dispatcher; + using Subscriber = subscription:: + SubscriberImpl; + + auto subscriber = std::make_shared( + manager->template getEngine(), std::move(initial)); + + subscriber->setCallback( + [f(std::forward(f))]( + auto, ObjectType &obj, uint64_t key, EventData data) mutable { + ASSERT_EQ(key, Event); + std::forward(f)(obj, std::move(data)); + }); + subscriber->template subscribe(0, Event); + return subscriber; + } + + using MockDispatcher = subscription::MockDispatcher; + using MockSubscriber = + subscription::MockSubscriber; + using TestEngine = subscription::SubscriptionEngine< + uint32_t, + MockDispatcher, + subscription::Subscriber>; + + auto createMockSubscriber(std::shared_ptr const &engine) { + return std::make_shared(engine); + } + + auto createTestEngine(std::shared_ptr const &dispatcher) { + return std::make_shared(dispatcher); + } + + auto createDispatcher() { + return std::make_shared(); + } +}; + +/** + * @given subscription engine + * @when subscriber is present + * @and notification is called + * @then subscriber must receive correct data from notification + */ +TEST_F(SubscriptionTest, SimpleExecutionTest) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + std::string test_value = "the fast and the furious"; + auto subscriber = createSubscriber<0ul, 1ull, std::string>( + manager, false, [&complete, test_value](auto, std::string value) { + ASSERT_EQ(test_value, value); + complete.set(); + }); + + manager->notify(1ull, test_value); + ASSERT_TRUE(complete.wait(std::chrono::minutes(1ull))); + + manager->dispose(); +} + +/** + * @given subscription engine + * @when subscriber is present in pool threads + * @and notification is called + * @then subscriber must receive correct data from notification + */ +TEST_F(SubscriptionTest, PoolSimpleExecutionTest) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + std::string test_value = "the fast and the furious"; + auto subscriber = + createSubscriber::max(), 1ull, std::string>( + manager, false, [&complete, test_value](auto, std::string value) { + ASSERT_EQ(test_value, value); + complete.set(); + }); + + manager->notify(1ull, test_value); + ASSERT_TRUE(complete.wait(std::chrono::minutes(1ull))); + + manager->dispose(); +} + +/** + * @given subscription engine with busy pool + * @when subscriber is present in pool threads + * @and notification is called + * @then subscriber must receive correct data from notification + */ +TEST_F(SubscriptionTest, BusyPoolSimpleExecutionTest) { + auto manager = createSubscriptionManager<1>(); + + utils::WaitForSingleObject complete; + utils::WaitForSingleObject complete1; + auto subscriber1 = + createSubscriber::max(), 1ull, bool>( + manager, false, [&complete, &complete1](auto, auto) { + complete1.set(); + complete.wait(); + complete.set(); + }); + + std::string test_value = "the fast and the furious"; + auto subscriber2 = + createSubscriber::max(), 2ull, std::string>( + manager, false, [&complete, test_value](auto, std::string value) { + ASSERT_EQ(test_value, value); + complete.set(); + }); + + manager->notify(1ull, false); + complete1.wait(); + manager->notify(2ull, test_value); + + ASSERT_TRUE(complete.wait(std::chrono::minutes(1ull))); + complete.set(); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and they subscribe to a single event in a single thread + * @then they must be called in the same order + */ +TEST_F(SubscriptionTest, DoubleExecutionTest) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + uint32_t counter = 0ul; + std::string test_value = "the fast and the furious"; + auto subscriber_1 = createSubscriber<0ul, 1ull, std::string>( + manager, false, [&counter, test_value](auto, std::string value) { + ASSERT_EQ(test_value, value); + ASSERT_EQ(counter, 0ul); + ++counter; + }); + + auto subscriber_2 = createSubscriber<0ul, 1ull, std::string>( + manager, + false, + [&complete, &counter, test_value](auto, std::string value) { + ASSERT_EQ(test_value, value); + ASSERT_EQ(counter, 1ul); + complete.set(); + }); + + manager->notify(1ull, test_value); + ASSERT_TRUE(complete.wait(std::chrono::minutes(1ull))); + + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and they subscribe to different events in a single thread + * @and both events are notified + * @then the handlers of each subscriber must be called once + */ +TEST_F(SubscriptionTest, XExecutionTest) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete[2]; + uint32_t counter[2] = {0ul}; + + [[maybe_unused]] auto subscriber_1 = + createSubscriber<0ul, 1ull, bool>(manager, false, [&](auto, auto) { + ASSERT_EQ(counter[0], 0ul); + ++counter[0]; + complete[0].set(); + }); + + [[maybe_unused]] auto subscriber_2 = + createSubscriber<0ul, 2ull, bool>(manager, false, [&](auto, auto) { + ASSERT_EQ(counter[1], 0ul); + ++counter[1]; + complete[1].set(); + }); + + manager->notify(1ull, false); + manager->notify(2ull, false); + ASSERT_TRUE(complete[0].wait(std::chrono::minutes(1ull))); + ASSERT_TRUE(complete[1].wait(std::chrono::minutes(1ull))); + + ASSERT_EQ(counter[0], 1ul); + ASSERT_EQ(counter[1], 1ul); + + manager->dispose(); +} + +/** + * @given subscription engine + * @when 4 subscribers are present + * @and they subscribe on a single event in a different threads + * @then each handler must be called once in his thread + */ +TEST_F(SubscriptionTest, ParallelExecutionTest) { + auto manager = createSubscriptionManager<4>(); + utils::WaitForSingleObject complete[4]; + + using SharedObject = + utils::ReadWriteObject>; + using SharedObjectRef = std::reference_wrapper; + + SharedObject shared_object; + + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 1ull, bool>( + manager, + SharedObjectRef(shared_object), + [&](SharedObjectRef object, auto) { + object.get().exclusiveAccess( + [](auto &data) { ++data[std::this_thread::get_id()]; }); + complete[0].set(); + }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<1ul, 1ull, bool>( + manager, + SharedObjectRef(shared_object), + [&](SharedObjectRef object, auto) { + object.get().exclusiveAccess( + [](auto &data) { ++data[std::this_thread::get_id()]; }); + complete[1].set(); + }); + [[maybe_unused]] auto subscriber_2 = createSubscriber<2ul, 1ull, bool>( + manager, + SharedObjectRef(shared_object), + [&](SharedObjectRef object, auto) { + object.get().exclusiveAccess( + [](auto &data) { ++data[std::this_thread::get_id()]; }); + complete[2].set(); + }); + [[maybe_unused]] auto subscriber_3 = createSubscriber<3ul, 1ull, bool>( + manager, + SharedObjectRef(shared_object), + [&](SharedObjectRef object, auto) { + object.get().exclusiveAccess( + [](auto &data) { ++data[std::this_thread::get_id()]; }); + complete[3].set(); + }); + + manager->notify(1ull, false); + for (auto &c : complete) ASSERT_TRUE(c.wait(std::chrono::minutes(1ull))); + + shared_object.sharedAccess([](auto const &values) { + ASSERT_EQ(values.size(), 4ull); + for (auto &value : values) ASSERT_TRUE(value.second == 1); + }); + + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and they subscribe to different events in a different threads + * @and each of them generate event for the other + * @then each handler must be called one by one + */ +TEST_F(SubscriptionTest, PingPongExecutionTest) { + auto manager = createSubscriptionManager<2>(); + utils::WaitForSingleObject complete; + + [[maybe_unused]] auto subscriber_0 = + createSubscriber<0ul, 0ull, uint32_t, uint32_t>( + manager, 0ul, [&](uint32_t &obj, uint32_t value) { + obj = value; + manager->notify(1ull, value + 7ul); + }); + [[maybe_unused]] auto subscriber_1 = + createSubscriber<1ul, 1ull, uint32_t, uint32_t>( + manager, 0ul, [&](uint32_t &obj, uint32_t value) { + obj = value; + if (value > 40ul) + complete.set(); + else + manager->notify(0ull, (value << 1ul)); + }); + + manager->notify(0ull, 0ul); + ASSERT_TRUE(complete.wait(std::chrono::minutes(1ull))); + ASSERT_EQ(subscriber_0->get(), 42ul); + ASSERT_EQ(subscriber_1->get(), 49ul); + + manager->dispose(); +} + +/** + * @given subscription engine + * @when 3 subscribers are present + * @and they subscribe on a single event + * @and this event notifies several times in order A-B-C + * @then no rotation is present: first all handlers will process A, then B, then + * C + */ +TEST_F(SubscriptionTest, RotationExecutionTest_1) { + auto manager = createSubscriptionManager<1>(); + + std::atomic counter = 0; + std::string result; + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 0ull, std::string>( + manager, false, [&](auto, std::string value) { + result += value; + ++counter; + }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<0ul, 0ull, std::string>( + manager, false, [&](auto, std::string value) { + result += value; + ++counter; + }); + [[maybe_unused]] auto subscriber_2 = createSubscriber<0ul, 0ull, std::string>( + manager, false, [&](auto, std::string value) { + result += value; + ++counter; + }); + + manager->notify(0ull, std::string("A")); + manager->notify(0ull, std::string("B")); + manager->notify(0ull, std::string("C")); + + while (counter.load(std::memory_order_relaxed) < 9) + std::this_thread::sleep_for(std::chrono::milliseconds(1ull)); + + ASSERT_EQ(result, "AAABBBCCC"); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 3 subscribers are present + * @and they subscribe on a single event + * @and this event notifies several times + * @then no rotation is present: handlers will be called in a subscription order + */ +TEST_F(SubscriptionTest, RotationExecutionTest_2) { + auto manager = createSubscriptionManager<1>(); + + std::atomic counter = 0; + std::string result; + [[maybe_unused]] auto subscriber_0 = + createSubscriber<0ul, 0ull, bool>(manager, false, [&](auto, auto value) { + result += 'A'; + ++counter; + }); + [[maybe_unused]] auto subscriber_1 = + createSubscriber<0ul, 0ull, bool>(manager, false, [&](auto, auto value) { + result += 'B'; + ++counter; + }); + [[maybe_unused]] auto subscriber_2 = + createSubscriber<0ul, 0ull, bool>(manager, false, [&](auto, auto value) { + result += 'C'; + ++counter; + }); + + manager->notify(0ull, false); + manager->notify(0ull, false); + manager->notify(0ull, false); + + while (counter.load(std::memory_order_relaxed) < 9) + std::this_thread::sleep_for(std::chrono::milliseconds(1ull)); + + ASSERT_EQ(result, "ABCABCABC"); + manager->dispose(); +} + +/** + * @given subscription engine + * @when subscriber is present + * @and notifications are generated with delay + * @then the handlers must be called in a delay order + */ +TEST_F(SubscriptionTest, RotationExecutionTest_3) { + auto manager = createSubscriptionManager<1>(); + + std::atomic counter = 0; + std::string result; + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 0ull, std::string>( + manager, false, [&](auto, auto value) { + std::this_thread::sleep_for(std::chrono::milliseconds(5ull)); + result += value; + ++counter; + }); + + manager->notifyDelayed( + std::chrono::milliseconds(100ull), 0ull, std::string("E")); + manager->notifyDelayed( + std::chrono::milliseconds(30ull), 0ull, std::string("C")); + manager->notifyDelayed( + std::chrono::milliseconds(50ull), 0ull, std::string("D")); + manager->notify(0ull, std::string("A")); + manager->notify(0ull, std::string("B")); + + while (counter.load(std::memory_order_relaxed) < 5) + std::this_thread::sleep_for(std::chrono::milliseconds(10ull)); + + ASSERT_EQ(result, "ABCDE"); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 5 subscribers are present + * @and notifications generate one for the next one + * @then the handlers must be called in a determined order + */ +TEST_F(SubscriptionTest, StarExecutionTest) { + auto manager = createSubscriptionManager<5>(); + utils::WaitForSingleObject complete; + std::string result; + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 0ull, std::string>( + manager, false, [&](auto, auto value) { + result += value; + manager->notify(1ull, std::string("t")); + }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<1ul, 1ull, std::string>( + manager, false, [&](auto, auto value) { + result += value; + manager->notify(2ull, std::string("a")); + }); + [[maybe_unused]] auto subscriber_2 = createSubscriber<2ul, 2ull, std::string>( + manager, false, [&](auto, auto value) { + result += value; + manager->notify(3ull, std::string("r")); + }); + [[maybe_unused]] auto subscriber_3 = createSubscriber<3ul, 3ull, std::string>( + manager, false, [&](auto, auto value) { + result += value; + manager->notify(4ull, std::string("!")); + }); + [[maybe_unused]] auto subscriber_4 = createSubscriber<4ul, 4ull, std::string>( + manager, false, [&](auto, auto value) { + result += value; + complete.set(); + }); + + manager->notify(0ull, std::string("S")); + ASSERT_TRUE(complete.wait(std::chrono::seconds(10ull))); + + ASSERT_EQ(result, "Star!"); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and the first unsubscribes from all events + * @then his handler must be skipped + */ +TEST_F(SubscriptionTest, UnsubExecutionTest) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 0ull, bool>( + manager, false, [&](auto, auto) { ASSERT_FALSE("Must not be called!"); }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<0ul, 0ull, bool>( + manager, false, [&](auto, auto) { complete.set(); }); + + subscriber_0->unsubscribe(); + manager->notify(0ull, false); + ASSERT_TRUE(complete.wait(std::chrono::seconds(10ull))); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and the first unsubscribes from a specific set + * @then his handler must be skipped + */ +TEST_F(SubscriptionTest, UnsubExecutionTest_1) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 0ull, bool>( + manager, false, [&](auto, auto) { ASSERT_FALSE("Must not be called!"); }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<0ul, 0ull, bool>( + manager, false, [&](auto, auto) { complete.set(); }); + + subscriber_0->unsubscribe(0ul); + manager->notify(0ull, false); + ASSERT_TRUE(complete.wait(std::chrono::seconds(10ull))); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and the first unsubscribes from a specific set and event + * @then his handler must be skipped + */ +TEST_F(SubscriptionTest, UnsubExecutionTest_2) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 1ull, bool>( + manager, false, [&](auto, auto) { ASSERT_FALSE("Must not be called!"); }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<0ul, 1ull, bool>( + manager, false, [&](auto, auto) { complete.set(); }); + + subscriber_0->unsubscribe(0ul, 1ull); + manager->notify(1ull, false); + ASSERT_TRUE(complete.wait(std::chrono::seconds(10ull))); + manager->dispose(); +} + +/** + * @given subscription engine + * @when 2 subscribers are present + * @and the first unsubscribes from events he didn't subscribe to + * @then his handler must be called, because he is still subscribed + */ +TEST_F(SubscriptionTest, UnsubExecutionTest_3) { + auto manager = createSubscriptionManager<1>(); + utils::WaitForSingleObject complete; + + std::atomic_flag flag; + flag.clear(); + + [[maybe_unused]] auto subscriber_0 = createSubscriber<0ul, 1ull, bool>( + manager, false, [&](auto, auto) { flag.test_and_set(); }); + [[maybe_unused]] auto subscriber_1 = createSubscriber<0ul, 1ull, bool>( + manager, false, [&](auto, auto) { complete.set(); }); + + subscriber_0->unsubscribe(1ul); + subscriber_0->unsubscribe(0ul, 2ull); + manager->notify(1ull, false); + ASSERT_TRUE(complete.wait(std::chrono::seconds(10ull))); + ASSERT_TRUE(flag.test_and_set()); + manager->dispose(); +} + +/** + * @given subscription engine + * @when subscriber is present by a notification type 1 + * @and this notification takes place + * @then his handler must be called + */ +TEST_F(SubscriptionTest, Notify) { + auto dispatcher = createDispatcher(); + auto engine = createTestEngine(dispatcher); + auto subscriber = createMockSubscriber(engine); + + std::string test_data("test_data"); + uint32_t event_id = 10ul; + + subscriber->subscribe<1ull>(event_id); + EXPECT_CALL(*subscriber, on_notify(0ull, event_id, std::string(test_data))) + .Times(1); + engine->notify(event_id, test_data); +} + +/** + * @given subscription engine + * @when subscriber is present by a notification type 1 + * @and this notification takes place with delay + * @then his handler must be called + */ +TEST_F(SubscriptionTest, NotifyDelayed) { + auto dispatcher = createDispatcher(); + auto engine = createTestEngine(dispatcher); + auto subscriber = createMockSubscriber(engine); + + std::string test_data("test_data"); + uint32_t event_id = 10ul; + + subscriber->subscribe<1ull>(event_id); + EXPECT_CALL(*subscriber, on_notify(0ull, event_id, std::string(test_data))) + .Times(1); + engine->notifyDelayed(std::chrono::microseconds(10ull), event_id, test_data); +} + +/** + * @given subscription engine + * @when subscribers are present by notification types 1 and 2 + * @and this notification 1 takes place + * @then only 1 subscriber must be executed + */ +TEST_F(SubscriptionTest, Notify_1) { + auto dispatcher = createDispatcher(); + auto engine = createTestEngine(dispatcher); + auto subscriber1 = createMockSubscriber(engine); + auto subscriber2 = createMockSubscriber(engine); + + std::string test_data("test_data"); + uint32_t event_id = 10ul; + uint32_t event_id_fake = 11ul; + + subscriber1->subscribe<1ull>(event_id); + subscriber2->subscribe<1ull>(event_id_fake); + + EXPECT_CALL(*subscriber1, on_notify(0ull, event_id, std::string(test_data))) + .Times(1); + EXPECT_CALL(*subscriber2, + on_notify(0ull, event_id_fake, std::string(test_data))) + .Times(0); + engine->notify(event_id, test_data); +} + +/** + * @given subscription engine + * @when subscribers are present by the same notification + * @and this notification takes place + * @then both of the subscribers must be executed + */ +TEST_F(SubscriptionTest, Notify_2) { + auto dispatcher = createDispatcher(); + auto engine = createTestEngine(dispatcher); + auto subscriber1 = createMockSubscriber(engine); + auto subscriber2 = createMockSubscriber(engine); + + std::string test_data("test_data"); + uint32_t event_id = 10ul; + + subscriber1->subscribe<1ull>(event_id); + subscriber2->subscribe<1ull>(event_id); + + EXPECT_CALL(*subscriber1, on_notify(0ull, event_id, std::string(test_data))) + .Times(1); + EXPECT_CALL(*subscriber2, on_notify(0ull, event_id, std::string(test_data))) + .Times(1); + engine->notify(event_id, test_data); +} From 5398a01e4153b1c30dcfdf2e7ced9c1ccdec49d9 Mon Sep 17 00:00:00 2001 From: Ivan Kuvaldin Date: Thu, 1 Apr 2021 16:53:48 +0300 Subject: [PATCH 19/86] Fix postgres_options_test Signed-off-by: kuvaldini Signed-off-by: Ivan Kuvaldin --- test/module/irohad/ametsuchi/postgres_options_test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/module/irohad/ametsuchi/postgres_options_test.cpp b/test/module/irohad/ametsuchi/postgres_options_test.cpp index 3a047e6eff1..181a6bed117 100644 --- a/test/module/irohad/ametsuchi/postgres_options_test.cpp +++ b/test/module/irohad/ametsuchi/postgres_options_test.cpp @@ -18,6 +18,7 @@ static const logger::LoggerPtr test_log = getTestLoggerManager()->getChild("PostgresOptions")->getLogger(); static const std::string default_working_dbname{"working_dbname"}; +static const std::string default_maintenance_dbname{"postgres"}; /** * Check that the given connection string contains field=value entry. @@ -120,9 +121,8 @@ TEST(PostgresOptionsTest, DBnameParamExist) { "1991", "petya", "friend", - dbname, // TODO 2019.06.26 mboldyrev IR-556 change dbname - // to default_working_dbname - "petya"); + dbname, + default_maintenance_dbname); } /** @@ -140,7 +140,7 @@ TEST(PostgresOptionsTest, DBnameParamNotExist) { PostgresOptions(pg_opt_string, default_working_dbname, test_log); checkPgOpts( - pg_opt, "down", "1991", "crab", "friend", default_working_dbname, "crab"); + pg_opt, "down", "1991", "crab", "friend", default_working_dbname, default_maintenance_dbname); } /** From f6d67b2267c854404003d2d2ee1b825b67b39640 Mon Sep 17 00:00:00 2001 From: kuvaldini Date: Wed, 7 Apr 2021 12:07:42 +0300 Subject: [PATCH 20/86] Clang-format Signed-off-by: kuvaldini --- irohad/ametsuchi/impl/postgres_options.cpp | 21 ++++++++++--------- .../ametsuchi/postgres_options_test.cpp | 14 +++++++++---- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/irohad/ametsuchi/impl/postgres_options.cpp b/irohad/ametsuchi/impl/postgres_options.cpp index 57937052d62..a50d249f911 100644 --- a/irohad/ametsuchi/impl/postgres_options.cpp +++ b/irohad/ametsuchi/impl/postgres_options.cpp @@ -5,12 +5,12 @@ #include "ametsuchi/impl/postgres_options.hpp" +#include +#include #include #include #include -#include -#include #include "logger/logger.hpp" using namespace iroha::ametsuchi; @@ -55,13 +55,13 @@ PostgresOptions::PostgresOptions(const std::string &pg_opt, std::string default_dbname, logger::LoggerPtr log) : PostgresOptions( - extractField(pg_opt, "host"), - getPort(extractField(pg_opt, "port")), - extractField(pg_opt, "user"), - extractField(pg_opt, "password"), - extractOptionalField(pg_opt, "dbname").value_or(default_dbname), - extractOptionalField(pg_opt, "maintenance_dbname").value_or("postgres"), - std::move(log)) {} + extractField(pg_opt, "host"), + getPort(extractField(pg_opt, "port")), + extractField(pg_opt, "user"), + extractField(pg_opt, "password"), + extractOptionalField(pg_opt, "dbname").value_or(default_dbname), + extractOptionalField(pg_opt, "maintenance_dbname").value_or("postgres"), + std::move(log)) {} PostgresOptions::PostgresOptions(const std::string &host, uint16_t port, @@ -87,7 +87,8 @@ PostgresOptions::PostgresOptions(const std::string &host, std::string PostgresOptions::connectionStringWithoutDbName() const { return (boost::format("host=%1% port=%2% user=%3% password=%4%") % host_ - % port_ % user_ % password_).str(); + % port_ % user_ % password_) + .str(); } std::string PostgresOptions::workingConnectionString() const { diff --git a/test/module/irohad/ametsuchi/postgres_options_test.cpp b/test/module/irohad/ametsuchi/postgres_options_test.cpp index 181a6bed117..c551f39430d 100644 --- a/test/module/irohad/ametsuchi/postgres_options_test.cpp +++ b/test/module/irohad/ametsuchi/postgres_options_test.cpp @@ -5,10 +5,11 @@ #include "ametsuchi/impl/postgres_options.hpp" -#include - #include + #include +#include + #include "framework/test_logger.hpp" #include "logger/logger_manager.hpp" @@ -139,8 +140,13 @@ TEST(PostgresOptionsTest, DBnameParamNotExist) { auto pg_opt = PostgresOptions(pg_opt_string, default_working_dbname, test_log); - checkPgOpts( - pg_opt, "down", "1991", "crab", "friend", default_working_dbname, default_maintenance_dbname); + checkPgOpts(pg_opt, + "down", + "1991", + "crab", + "friend", + default_working_dbname, + default_maintenance_dbname); } /** From 18b6518f3a5361f6a409ed2db7ac0e6fe84de871 Mon Sep 17 00:00:00 2001 From: iceseer Date: Sun, 11 Apr 2021 16:10:23 +0300 Subject: [PATCH 21/86] [SE] do not allow stack-created subscriber Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Signed-off-by: iceseer --- irohad/main/subscription.hpp | 19 +++++++++++-------- irohad/subscription/subscriber_impl.hpp | 19 ++++++++++++++++--- .../irohad/subscription/subscription_test.cpp | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/irohad/main/subscription.hpp b/irohad/main/subscription.hpp index 8ee1a0e4c76..df97227c0e2 100644 --- a/irohad/main/subscription.hpp +++ b/irohad/main/subscription.hpp @@ -67,20 +67,23 @@ namespace iroha { std::shared_ptr getSubscription(); - template + template struct SubscriberCreator { - template - static auto create(F &&callback) { - auto subscriber = - std::make_shared>( - getSubscription()->getEngine()); + template + static auto create(F &&callback, Args &&... args) { + auto subscriber = BaseSubscriber::create( + getSubscription()->getEngine(), + std::forward(args)...); subscriber->setCallback( [f{std::forward(callback)}](auto /*set_id*/, auto &object, auto event_key, - EventData... args) { + EventData args) mutable { assert(key == event_key); - std::forward(f)(object, std::move(args)...); + std::forward(f)(object, std::move(args)); }); subscriber->template subscribe(0, key); return subscriber; diff --git a/irohad/subscription/subscriber_impl.hpp b/irohad/subscription/subscriber_impl.hpp index 0e236fc3430..229c6e914b1 100644 --- a/irohad/subscription/subscriber_impl.hpp +++ b/irohad/subscription/subscriber_impl.hpp @@ -32,8 +32,7 @@ namespace iroha::subscription { typename Dispatcher, typename Receiver, typename... Arguments> - class SubscriberImpl final - : public Subscriber { + class SubscriberImpl : public Subscriber { public: using ReceiverType = Receiver; using Hash = size_t; @@ -77,7 +76,6 @@ namespace iroha::subscription { /// Stored notification callback CallbackFnType on_notify_callback_; - public: template SubscriberImpl(SubscriptionEnginePtr const &ptr, SubscriberConstructorArgs &&... args) @@ -85,6 +83,21 @@ namespace iroha::subscription { engine_(ptr), object_(std::forward(args)...) {} + public: + template + static std::shared_ptr create( + SubscriptionEnginePtr const &ptr, + SubscriberConstructorArgs &&... args) { + struct Resolver : SubscriberImpl { + Resolver(SubscriptionEnginePtr const &ptr, + SubscriberConstructorArgs &&... args) + : SubscriberImpl( + ptr, std::forward(args)...) {} + }; + return std::make_shared( + ptr, std::forward(args)...); + } + ~SubscriberImpl() {} void setCallback(CallbackFnType &&f) { diff --git a/test/module/irohad/subscription/subscription_test.cpp b/test/module/irohad/subscription/subscription_test.cpp index 7b83ef54cc8..3d835839ab3 100644 --- a/test/module/irohad/subscription/subscription_test.cpp +++ b/test/module/irohad/subscription/subscription_test.cpp @@ -33,7 +33,7 @@ class SubscriptionTest : public ::testing::Test { using Subscriber = subscription:: SubscriberImpl; - auto subscriber = std::make_shared( + auto subscriber = Subscriber::create( manager->template getEngine(), std::move(initial)); subscriber->setCallback( From 25557834dcffa4588b5ea0d6650ed06db1d1ed8a Mon Sep 17 00:00:00 2001 From: Andrei Lebedev Date: Sat, 10 Apr 2021 14:56:51 +0200 Subject: [PATCH 22/86] AmetsuchiTest: add condition for prepared block tests Signed-off-by: Andrei Lebedev --- test/module/irohad/ametsuchi/ametsuchi_fixture.hpp | 6 ++++++ test/module/irohad/ametsuchi/ametsuchi_test.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp index 27f091710d9..39cac3831e1 100644 --- a/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp +++ b/test/module/irohad/ametsuchi/ametsuchi_fixture.hpp @@ -98,6 +98,9 @@ namespace iroha { truncateWsv(); } + prepared_blocks_enabled = + pool_wrapper->enable_prepared_transactions_; + return StorageImpl::create( *options_, std::move(pool_wrapper), @@ -234,6 +237,8 @@ namespace iroha { static std::unique_ptr options_; static std::string block_store_path; + + static bool prepared_blocks_enabled; }; std::shared_ptr Date: Fri, 16 Apr 2021 13:12:33 +0300 Subject: [PATCH 23/86] [SE] sync/async dispatcher Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Signed-off-by: iceseer --- irohad/main/CMakeLists.txt | 14 ++- irohad/main/impl/async_dispatcher.cpp | 18 ++++ irohad/main/impl/subscription.cpp | 2 +- irohad/main/impl/sync_dispatcher.cpp | 18 ++++ irohad/main/subscription.hpp | 15 +-- irohad/subscription/async_dispatcher_impl.hpp | 96 +++++++++++++++++++ irohad/subscription/dispatcher.hpp | 85 +++------------- irohad/subscription/subscription_manager.hpp | 5 +- irohad/subscription/sync_dispatcher_impl.hpp | 42 ++++++++ .../module/irohad/subscription/CMakeLists.txt | 2 +- .../irohad/subscription/subscription_test.cpp | 4 +- 11 files changed, 211 insertions(+), 90 deletions(-) create mode 100644 irohad/main/impl/async_dispatcher.cpp create mode 100644 irohad/main/impl/sync_dispatcher.cpp create mode 100644 irohad/subscription/async_dispatcher_impl.hpp create mode 100644 irohad/subscription/sync_dispatcher_impl.hpp diff --git a/irohad/main/CMakeLists.txt b/irohad/main/CMakeLists.txt index d8d58a1f668..1e6bc01fbf7 100644 --- a/irohad/main/CMakeLists.txt +++ b/irohad/main/CMakeLists.txt @@ -35,9 +35,13 @@ target_link_libraries(pg_connection_init logger ) -add_library(subscription impl/subscription.cpp) -target_link_libraries(subscription - ) +add_library(async_subscription + impl/subscription.cpp + impl/async_dispatcher.cpp) + +add_library(sync_subscription + impl/subscription.cpp + impl/sync_dispatcher.cpp) add_library(pending_txs_storage_init impl/pending_transaction_storage_init.cpp) target_link_libraries(pending_txs_storage_init @@ -87,7 +91,7 @@ target_link_libraries(application common pg_connection_init generator - subscription + async_subscription ) add_executable(irohad irohad.cpp) @@ -106,7 +110,7 @@ target_link_libraries(irohad logger_manager irohad_version pg_connection_init - subscription + async_subscription maintenance ) diff --git a/irohad/main/impl/async_dispatcher.cpp b/irohad/main/impl/async_dispatcher.cpp new file mode 100644 index 00000000000..509e8fb2859 --- /dev/null +++ b/irohad/main/impl/async_dispatcher.cpp @@ -0,0 +1,18 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "main/subscription.hpp" + +#include "subscription/async_dispatcher_impl.hpp" + +namespace iroha { + + std::shared_ptr getDispatcher() { + return std::make_shared< + subscription::AsyncDispatcher>(); + } + +} // namespace iroha diff --git a/irohad/main/impl/subscription.cpp b/irohad/main/impl/subscription.cpp index a46f708cab0..925bbbbb7da 100644 --- a/irohad/main/impl/subscription.cpp +++ b/irohad/main/impl/subscription.cpp @@ -19,7 +19,7 @@ namespace iroha { if (auto ptr = engine.lock()) return ptr; - auto ptr = std::make_shared(); + auto ptr = std::make_shared(getDispatcher()); engine = ptr; return ptr; } diff --git a/irohad/main/impl/sync_dispatcher.cpp b/irohad/main/impl/sync_dispatcher.cpp new file mode 100644 index 00000000000..93f9f8215d6 --- /dev/null +++ b/irohad/main/impl/sync_dispatcher.cpp @@ -0,0 +1,18 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "main/subscription.hpp" + +#include "subscription/sync_dispatcher_impl.hpp" + +namespace iroha { + + std::shared_ptr getDispatcher() { + return std::make_shared< + subscription::SyncDispatcher>(); + } + +} // namespace iroha diff --git a/irohad/main/subscription.hpp b/irohad/main/subscription.hpp index df97227c0e2..8cfc6437f2d 100644 --- a/irohad/main/subscription.hpp +++ b/irohad/main/subscription.hpp @@ -54,17 +54,20 @@ namespace iroha { static constexpr uint32_t kThreadPoolSize = 3u; + using Dispatcher = + subscription::IDispatcher; using Subscription = subscription::SubscriptionManager; - using SubscriptionDispatcher = typename Subscription::Dispatcher; - template - using BaseSubscriber = subscription::SubscriberImpl; + using BaseSubscriber = + subscription::SubscriberImpl; + std::shared_ptr getDispatcher(); std::shared_ptr getSubscription(); template diff --git a/irohad/subscription/async_dispatcher_impl.hpp b/irohad/subscription/async_dispatcher_impl.hpp new file mode 100644 index 00000000000..019edb5b21c --- /dev/null +++ b/irohad/subscription/async_dispatcher_impl.hpp @@ -0,0 +1,96 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_ASYNC_DISPATCHER_IMPL_HPP +#define IROHA_SUBSCRIPTION_ASYNC_DISPATCHER_IMPL_HPP + +#include "subscription/dispatcher.hpp" + +#include "subscription/common.hpp" +#include "subscription/thread_handler.hpp" + +namespace iroha::subscription { + + template + class AsyncDispatcher final : public IDispatcher, + utils::NoCopy, + utils::NoMove { + private: + using Parent = IDispatcher; + + struct ThreadHandlerContext { + /// Worker thread to execute tasks + std::shared_ptr handler; + + /// Shows if this handler is static or if it was created to + /// execute a single task and should be deleted after performing it + bool is_temporary; + }; + + ThreadHandlerContext handlers_[Parent::kHandlersCount]; + ThreadHandlerContext pool_[Parent::kPoolThreadsCount]; + + inline ThreadHandlerContext findHandler(typename Parent::Tid const tid) { + assert(tid < Parent::kHandlersCount || tid == Parent::kExecuteInPool); + if (tid < Parent::kHandlersCount) + return handlers_[tid]; + + for (auto &handler : pool_) + if (!handler.handler->isBusy()) + return handler; + + return ThreadHandlerContext{ + std::make_shared(), + true // temporary + }; + } + + public: + AsyncDispatcher() { + for (auto &h : handlers_) { + h.handler = std::make_shared(); + h.is_temporary = false; + } + for (auto &h : pool_) { + h.handler = std::make_shared(); + h.is_temporary = false; + } + } + + void dispose() override { + for (auto &h : handlers_) h.handler->dispose(); + for (auto &h : pool_) h.handler->dispose(); + } + + void add(typename Parent::Tid tid, typename Parent::Task &&task) override { + auto h = findHandler(tid); + if (!h.is_temporary) + h.handler->add(std::move(task)); + else { + h.handler->add([h, task{std::move(task)}]() mutable { + task(); + h.handler->dispose(false); + }); + } + } + + void addDelayed(typename Parent::Tid tid, + std::chrono::microseconds timeout, + typename Parent::Task &&task) override { + auto h = findHandler(tid); + if (!h.is_temporary) + h.handler->addDelayed(timeout, std::move(task)); + else { + h.handler->addDelayed(timeout, [h, task{std::move(task)}]() mutable { + task(); + h.handler->dispose(false); + }); + } + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_ASYNC_DISPATCHER_IMPL_HPP diff --git a/irohad/subscription/dispatcher.hpp b/irohad/subscription/dispatcher.hpp index aa4aa33b49a..b63a9413376 100644 --- a/irohad/subscription/dispatcher.hpp +++ b/irohad/subscription/dispatcher.hpp @@ -12,58 +12,15 @@ namespace iroha::subscription { template - class Dispatcher final : utils::NoCopy, utils::NoMove { - public: - static constexpr uint32_t kHandlersCount = kCount; - static constexpr uint32_t kPoolThreadsCount = kPoolSize; - using Task = ThreadHandler::Task; + struct IDispatcher { using Tid = uint32_t; - static constexpr Tid kExecuteInPool = std::numeric_limits::max(); - - private: - struct ThreadHandlerContext { - /// Worker thread to execute tasks - std::shared_ptr handler; - - /// Shows if this handler is static or if it was created to - /// execute a single task and should be deleted after performing it - bool is_temporary; - }; - - ThreadHandlerContext handlers_[kHandlersCount]; - ThreadHandlerContext pool_[kPoolThreadsCount]; - - inline ThreadHandlerContext findHandler(Tid const tid) { - assert(tid < kHandlersCount || tid == kExecuteInPool); - if (tid < kHandlersCount) - return handlers_[tid]; - - for (auto &handler : pool_) - if (!handler.handler->isBusy()) - return handler; - - return ThreadHandlerContext{ - std::make_shared(), - true // temporary - }; - } + using Task = ThreadHandler::Task; - public: - Dispatcher() { - for (auto &h : handlers_) { - h.handler = std::make_shared(); - h.is_temporary = false; - } - for (auto &h : pool_) { - h.handler = std::make_shared(); - h.is_temporary = false; - } - } + static constexpr Tid kExecuteInPool = std::numeric_limits::max(); + static constexpr uint32_t kHandlersCount = kCount; + static constexpr uint32_t kPoolThreadsCount = kPoolSize; - void dispose() { - for (auto &h : handlers_) h.handler->dispose(); - for (auto &h : pool_) h.handler->dispose(); - } + virtual ~IDispatcher() {} template static constexpr void checkTid() { @@ -71,31 +28,11 @@ namespace iroha::subscription { "Unexpected TID handler."); } - template - void add(Tid tid, F &&f) { - auto h = findHandler(tid); - if (!h.is_temporary) - h.handler->add(std::forward(f)); - else { - h.handler->add([h, f{std::forward(f)}]() mutable { - std::forward(f)(); - h.handler->dispose(false); - }); - } - } - - template - void addDelayed(Tid tid, std::chrono::microseconds timeout, F &&f) { - auto h = findHandler(tid); - if (!h.is_temporary) - h.handler->addDelayed(timeout, std::forward(f)); - else { - h.handler->addDelayed(timeout, [h, f{std::forward(f)}]() mutable { - std::forward(f)(); - h.handler->dispose(false); - }); - } - } + virtual void dispose() = 0; + virtual void add(Tid tid, Task &&task) = 0; + virtual void addDelayed(Tid tid, + std::chrono::microseconds timeout, + Task &&task) = 0; }; } // namespace iroha::subscription diff --git a/irohad/subscription/subscription_manager.hpp b/irohad/subscription/subscription_manager.hpp index 2e81b5bd0ab..f160ce6ac66 100644 --- a/irohad/subscription/subscription_manager.hpp +++ b/irohad/subscription/subscription_manager.hpp @@ -31,7 +31,7 @@ namespace iroha::subscription { utils::NoMove, utils::NoCopy { public: - using Dispatcher = subscription::Dispatcher; + using Dispatcher = subscription::IDispatcher; private: using EngineHash = uint64_t; @@ -58,7 +58,8 @@ namespace iroha::subscription { } public: - SubscriptionManager() : dispatcher_(std::make_shared()) { + SubscriptionManager(DispatcherPtr dispatcher) + : dispatcher_(std::move(dispatcher)) { disposed_.clear(); } diff --git a/irohad/subscription/sync_dispatcher_impl.hpp b/irohad/subscription/sync_dispatcher_impl.hpp new file mode 100644 index 00000000000..789636dea08 --- /dev/null +++ b/irohad/subscription/sync_dispatcher_impl.hpp @@ -0,0 +1,42 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SYNC_DISPATCHER_IMPL_HPP +#define IROHA_SUBSCRIPTION_SYNC_DISPATCHER_IMPL_HPP + +#include "subscription/dispatcher.hpp" + +#include "subscription/common.hpp" +#include "subscription/thread_handler.hpp" + +namespace iroha::subscription { + + template + class SyncDispatcher final : public IDispatcher, + utils::NoCopy, + utils::NoMove { + private: + using Parent = IDispatcher; + + public: + SyncDispatcher() = default; + + void dispose() override {} + + void add(typename Parent::Tid /*tid*/, + typename Parent::Task &&task) override { + task(); + } + + void addDelayed(typename Parent::Tid /*tid*/, + std::chrono::microseconds /*timeout*/, + typename Parent::Task &&task) override { + task(); + } + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SYNC_DISPATCHER_IMPL_HPP diff --git a/test/module/irohad/subscription/CMakeLists.txt b/test/module/irohad/subscription/CMakeLists.txt index c0f50602f59..5a5878a0fd2 100644 --- a/test/module/irohad/subscription/CMakeLists.txt +++ b/test/module/irohad/subscription/CMakeLists.txt @@ -5,5 +5,5 @@ addtest(subscription_test subscription_test.cpp) target_link_libraries(subscription_test - subscription + async_subscription ) diff --git a/test/module/irohad/subscription/subscription_test.cpp b/test/module/irohad/subscription/subscription_test.cpp index 3d835839ab3..b334af4a7b6 100644 --- a/test/module/irohad/subscription/subscription_test.cpp +++ b/test/module/irohad/subscription/subscription_test.cpp @@ -9,6 +9,7 @@ #include #include "module/irohad/subscription/subscription_mocks.hpp" +#include "subscription/async_dispatcher_impl.hpp" using namespace iroha; @@ -17,7 +18,8 @@ class SubscriptionTest : public ::testing::Test { template auto createSubscriptionManager() { using Manager = subscription::SubscriptionManager; - return std::make_shared(); + return std::make_shared( + std::make_shared>()); } template Date: Tue, 27 Apr 2021 11:08:01 +0300 Subject: [PATCH 24/86] added schedulers bind/unbind additional schedulers to dispatcher Signed-off-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Signed-off-by: iceseer --- irohad/main/subscription.hpp | 15 +- irohad/subscription/async_dispatcher_impl.hpp | 61 ++++- irohad/subscription/dispatcher.hpp | 17 +- irohad/subscription/scheduler.hpp | 35 +++ irohad/subscription/scheduler_impl.hpp | 163 +++++++++++++ irohad/subscription/subscriber_impl.hpp | 8 +- irohad/subscription/subscription_engine.hpp | 10 +- irohad/subscription/subscription_manager.hpp | 3 +- irohad/subscription/sync_dispatcher_impl.hpp | 15 +- irohad/subscription/thread_handler.hpp | 139 +---------- .../subscription/subscription_mocks.hpp | 6 +- .../irohad/subscription/subscription_test.cpp | 224 +++++++++++------- 12 files changed, 434 insertions(+), 262 deletions(-) create mode 100644 irohad/subscription/scheduler.hpp create mode 100644 irohad/subscription/scheduler_impl.hpp diff --git a/irohad/main/subscription.hpp b/irohad/main/subscription.hpp index 8cfc6437f2d..488675f1dc4 100644 --- a/irohad/main/subscription.hpp +++ b/irohad/main/subscription.hpp @@ -54,9 +54,7 @@ namespace iroha { static constexpr uint32_t kThreadPoolSize = 3u; - using Dispatcher = - subscription::IDispatcher; + using Dispatcher = subscription::IDispatcher; using Subscription = subscription::SubscriptionManager; @@ -72,11 +70,10 @@ namespace iroha { template struct SubscriberCreator { - template - static auto create(F &&callback, Args &&... args) { + template + static auto create(SubscriptionEngineHandlers tid, + F &&callback, + Args &&... args) { auto subscriber = BaseSubscriber::create( getSubscription()->getEngine(), std::forward(args)...); @@ -88,7 +85,7 @@ namespace iroha { assert(key == event_key); std::forward(f)(object, std::move(args)); }); - subscriber->template subscribe(0, key); + subscriber->subscribe(0, key, tid); return subscriber; } }; diff --git a/irohad/subscription/async_dispatcher_impl.hpp b/irohad/subscription/async_dispatcher_impl.hpp index 019edb5b21c..6a0d55666fd 100644 --- a/irohad/subscription/async_dispatcher_impl.hpp +++ b/irohad/subscription/async_dispatcher_impl.hpp @@ -14,34 +14,53 @@ namespace iroha::subscription { template - class AsyncDispatcher final : public IDispatcher, + class AsyncDispatcher final : public IDispatcher, utils::NoCopy, utils::NoMove { + public: + static constexpr uint32_t kHandlersCount = kCount; + static constexpr uint32_t kPoolThreadsCount = kPoolSize; + private: - using Parent = IDispatcher; + using Parent = IDispatcher; - struct ThreadHandlerContext { - /// Worker thread to execute tasks - std::shared_ptr handler; + struct SchedulerContext { + /// Scheduler to execute tasks + std::shared_ptr handler; /// Shows if this handler is static or if it was created to /// execute a single task and should be deleted after performing it bool is_temporary; }; - ThreadHandlerContext handlers_[Parent::kHandlersCount]; - ThreadHandlerContext pool_[Parent::kPoolThreadsCount]; + SchedulerContext handlers_[kHandlersCount]; + SchedulerContext pool_[kPoolThreadsCount]; - inline ThreadHandlerContext findHandler(typename Parent::Tid const tid) { - assert(tid < Parent::kHandlersCount || tid == Parent::kExecuteInPool); - if (tid < Parent::kHandlersCount) + struct BoundContexts { + typename Parent::Tid next_tid_offset = 0u; + std::unordered_map contexts; + }; + utils::ReadWriteObject bound_; + + inline SchedulerContext findHandler(typename Parent::Tid const tid) { + if (tid < kHandlersCount) return handlers_[tid]; + if (auto context = + bound_.sharedAccess([tid](BoundContexts const &bound) + -> std::optional { + if (auto it = bound.contexts.find(tid); + it != bound.contexts.end()) + return it->second; + return std::nullopt; + })) + return *context; + for (auto &handler : pool_) if (!handler.handler->isBusy()) return handler; - return ThreadHandlerContext{ + return SchedulerContext{ std::make_shared(), true // temporary }; @@ -89,6 +108,26 @@ namespace iroha::subscription { }); } } + + std::optional bind(std::shared_ptr scheduler) override { + if (!scheduler) + return std::nullopt; + + return bound_.exclusiveAccess( + [scheduler(std::move(scheduler))](BoundContexts &bound) { + auto const execution_tid = kHandlersCount + bound.next_tid_offset; + assert(bound.contexts.find(execution_tid) == bound.contexts.end()); + bound.contexts[execution_tid] = SchedulerContext{scheduler, false}; + ++bound.next_tid_offset; + return execution_tid; + }); + } + + bool unbind(Tid tid) override { + return bound_.exclusiveAccess([tid](BoundContexts &bound) { + return bound.contexts.erase(tid) == 1; + }); + } }; } // namespace iroha::subscription diff --git a/irohad/subscription/dispatcher.hpp b/irohad/subscription/dispatcher.hpp index b63a9413376..8644c5944f3 100644 --- a/irohad/subscription/dispatcher.hpp +++ b/irohad/subscription/dispatcher.hpp @@ -6,27 +6,22 @@ #ifndef IROHA_SUBSCRIPTION_DISPATCHER_HPP #define IROHA_SUBSCRIPTION_DISPATCHER_HPP +#include + #include "subscription/common.hpp" -#include "subscription/thread_handler.hpp" +#include "subscription/scheduler.hpp" namespace iroha::subscription { - template struct IDispatcher { using Tid = uint32_t; - using Task = ThreadHandler::Task; - + using Task = IScheduler::Task; static constexpr Tid kExecuteInPool = std::numeric_limits::max(); - static constexpr uint32_t kHandlersCount = kCount; - static constexpr uint32_t kPoolThreadsCount = kPoolSize; virtual ~IDispatcher() {} - template - static constexpr void checkTid() { - static_assert(kId < kHandlersCount || kId == kExecuteInPool, - "Unexpected TID handler."); - } + virtual std::optional bind(std::shared_ptr scheduler) = 0; + virtual bool unbind(Tid tid) = 0; virtual void dispose() = 0; virtual void add(Tid tid, Task &&task) = 0; diff --git a/irohad/subscription/scheduler.hpp b/irohad/subscription/scheduler.hpp new file mode 100644 index 00000000000..b7e93507ecc --- /dev/null +++ b/irohad/subscription/scheduler.hpp @@ -0,0 +1,35 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SCHEDULER_HPP +#define IROHA_SUBSCRIPTION_SCHEDULER_HPP + +#include + +#include "subscription/common.hpp" + +namespace iroha::subscription { + + class IScheduler { + public: + using Task = std::function; + virtual ~IScheduler() {} + + /// Stops sheduler work and tasks execution + virtual void dispose(bool wait_for_release = true) = 0; + + /// Checks if current scheduler executes task + virtual bool isBusy() const = 0; + + /// Adds task to execution queue + virtual void add(Task &&t) = 0; + + /// Adds delayed task to execution queue + virtual void addDelayed(std::chrono::microseconds timeout, Task &&t) = 0; + }; + +} // namespace iroha::subscription + +#endif // IROHA_SUBSCRIPTION_SCHEDULER_HPP diff --git a/irohad/subscription/scheduler_impl.hpp b/irohad/subscription/scheduler_impl.hpp new file mode 100644 index 00000000000..16536586f23 --- /dev/null +++ b/irohad/subscription/scheduler_impl.hpp @@ -0,0 +1,163 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_SUBSCRIPTION_SCHEDULER_IMPL_HPP +#define IROHA_SUBSCRIPTION_SCHEDULER_IMPL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "subscription/scheduler.hpp" + +#include "subscription/common.hpp" + +/** + * If you need to execute task, that was made in this thread and want to be + * executed in the same thread without delay - you need to uncomment this define + */ +//#define SE_SYNC_CALL_IF_SAME_THREAD + +namespace iroha::subscription { + + class SchedulerBase : public IScheduler, utils::NoCopy, utils::NoMove { + private: + using Time = std::chrono::high_resolution_clock; + using Timepoint = std::chrono::time_point