From e73420b683b617831e9a2a28c6810d85c0ac39cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Sun, 25 Feb 2024 13:03:05 +0100 Subject: [PATCH] Allow multiple queries per channel during warmup --- nano/lib/stats_enums.hpp | 1 + nano/node/repcrawler.cpp | 58 ++++++++++++++++++++++++---------------- nano/node/repcrawler.hpp | 4 +-- 3 files changed, 38 insertions(+), 25 deletions(-) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index fe15550b2f..edad231fd5 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -334,6 +334,7 @@ enum class detail : uint8_t query_target_failed, query_channel_busy, query_sent, + query_duplicate, rep_timeout, query_timeout, crawl_aggressive, diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index 2dded73005..bb5294ddc0 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -181,11 +181,10 @@ void nano::rep_crawler::run () { last_query = std::chrono::steady_clock::now (); - lock.unlock (); - auto targets = prepare_crawl_targets (sufficient_weight); - query (targets); + lock.unlock (); + query (targets); lock.lock (); } @@ -233,23 +232,27 @@ void nano::rep_crawler::cleanup () std::vector> nano::rep_crawler::prepare_crawl_targets (bool sufficient_weight) const { + debug_assert (!mutex.try_lock ()); + // TODO: Make these values configurable constexpr std::size_t conservative_count = 10; constexpr std::size_t aggressive_count = 40; + constexpr std::size_t conservative_max_attempts = 1; + constexpr std::size_t aggressive_max_attempts = 8; + + stats.inc (nano::stat::type::rep_crawler, sufficient_weight ? nano::stat::detail::crawl_normal : nano::stat::detail::crawl_aggressive); // Crawl more aggressively if we lack sufficient total peer weight. - auto required_peer_count = sufficient_weight ? conservative_count : aggressive_count; + auto const required_peer_count = sufficient_weight ? conservative_count : aggressive_count; - stats.inc (nano::stat::type::rep_crawler, sufficient_weight ? nano::stat::detail::crawl_normal : nano::stat::detail::crawl_aggressive); + auto random_peers = node.network.random_set (required_peer_count, 0, /* Include channels with ephemeral remote ports */ true); - // Add random peers. We do this even if we have enough weight, in order to pick up reps - // that didn't respond when first observed. If the current total weight isn't sufficient, this - // will be more aggressive. When the node first starts, the rep container is empty and all - // endpoints will originate from random peers. - required_peer_count += required_peer_count / 2; + // Avoid querying the same peer multiple times when rep crawler is warmed up + auto const max_attempts = sufficient_weight ? conservative_max_attempts : aggressive_max_attempts; + erase_if (random_peers, [this, max_attempts] (std::shared_ptr const & channel) { + return queries.get ().count (channel) >= max_attempts; + }); - // The rest of the endpoints are picked randomly - auto random_peers = node.network.random_set (required_peer_count, 0, true); // Include channels with ephemeral remote ports return { random_peers.begin (), random_peers.end () }; } @@ -292,12 +295,15 @@ auto nano::rep_crawler::prepare_query_target () -> std::optional return hash_root; } -void nano::rep_crawler::track_rep_request (hash_root_t hash_root, std::shared_ptr const & channel) +bool nano::rep_crawler::track_rep_request (hash_root_t hash_root, std::shared_ptr const & channel) { debug_assert (!mutex.try_lock ()); - debug_assert (queries.count (channel) == 0); // Only a single query should be active per channel - queries.emplace (query_entry{ hash_root.first, channel }); + auto [it, inserted] = queries.emplace (query_entry{ hash_root.first, channel }); + if (!inserted) + { + return false; // Duplicate, not tracked + } // Find and update the timestamp on all reps available on the endpoint (a single host may have multiple reps) auto & index = reps.get (); @@ -308,6 +314,8 @@ void nano::rep_crawler::track_rep_request (hash_root_t hash_root, std::shared_pt info.last_request = std::chrono::steady_clock::now (); }); } + + return true; } void nano::rep_crawler::query (std::vector> const & target_channels) @@ -315,6 +323,7 @@ void nano::rep_crawler::query (std::vectorto_string ()); @@ -337,7 +345,8 @@ void nano::rep_crawler::query (std::vectorto_string ()); + stats.inc (nano::stat::type::rep_crawler, nano::stat::detail::query_duplicate); } } } @@ -361,10 +370,13 @@ bool nano::rep_crawler::is_pr (std::shared_ptr const & bool nano::rep_crawler::process (std::shared_ptr const & vote, std::shared_ptr const & channel) { nano::lock_guard lock{ mutex }; - if (auto info = queries.find (channel); info != queries.end ()) + + auto & index = queries.get (); + auto [begin, end] = index.equal_range (channel); + for (auto it = begin; it != end; ++it) { // TODO: This linear search could be slow, especially with large votes. - auto const target_hash = info->hash; + auto const target_hash = it->hash; bool found = std::any_of (vote->hashes.begin (), vote->hashes.end (), [&target_hash] (nano::block_hash const & hash) { return hash == target_hash; }); @@ -376,10 +388,10 @@ bool nano::rep_crawler::process (std::shared_ptr const & vote, std:: // TODO: Track query response time responses.push_back ({ channel, vote }); - queries.erase (info); + queries.erase (it); condition.notify_all (); - return true; + return true; // Found and processed } } return false; diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 48c8bda3a0..0b20530475 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -111,7 +111,7 @@ class rep_crawler final /** Returns a list of endpoints to crawl. The total weight is passed in to avoid computing it twice. */ std::vector> prepare_crawl_targets (bool sufficient_weight) const; std::optional prepare_query_target (); - void track_rep_request (hash_root_t hash_root, std::shared_ptr const & channel_a); + bool track_rep_request (hash_root_t hash_root, std::shared_ptr const & channel_a); private: /** @@ -162,7 +162,7 @@ class rep_crawler final using ordered_queries = boost::multi_index_container, + mi::hashed_non_unique, mi::member, &query_entry::channel>>, mi::sequenced>, mi::hashed_non_unique,