From 5f31cb131c96a79678b29c998809a4b45cadbd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Tue, 29 Oct 2024 22:11:16 +0100 Subject: [PATCH] Rate limit ascending bootstrap requests --- nano/node/bootstrap/bootstrap_config.cpp | 2 ++ nano/node/bootstrap/bootstrap_config.hpp | 3 ++- nano/node/bootstrap/bootstrap_service.cpp | 28 +++++++++++------------ nano/node/bootstrap/bootstrap_service.hpp | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/nano/node/bootstrap/bootstrap_config.cpp b/nano/node/bootstrap/bootstrap_config.cpp index 9872c8b451..b3e2e99b0c 100644 --- a/nano/node/bootstrap/bootstrap_config.cpp +++ b/nano/node/bootstrap/bootstrap_config.cpp @@ -37,6 +37,7 @@ nano::error nano::bootstrap_config::deserialize (nano::tomlconfig & toml) toml.get ("enable_frontier_scan", enable_frontier_scan); toml.get ("channel_limit", channel_limit); + toml.get ("rate_limit", rate_limit); toml.get ("database_rate_limit", database_rate_limit); toml.get ("database_warmup_ratio", database_warmup_ratio); toml.get ("max_pull_count", max_pull_count); @@ -64,6 +65,7 @@ nano::error nano::bootstrap_config::serialize (nano::tomlconfig & toml) const toml.put ("enable_frontier_scan", enable_frontier_scan, "Enable or disable the 'frontier scan` strategy for the ascending bootstrap.\ntype:bool"); toml.put ("channel_limit", channel_limit, "Maximum number of un-responded requests per channel.\nNote: changing to unlimited (0) is not recommended.\ntype:uint64"); + toml.put ("rate_limit", rate_limit, "Rate limit on requests.\nNote: changing to unlimited (0) is not recommended as this operation competes for resources with realtime traffic.\ntype:uint64"); toml.put ("database_rate_limit", database_rate_limit, "Rate limit on scanning accounts and pending entries from database.\nNote: changing to unlimited (0) is not recommended as this operation competes for resources on querying the database.\ntype:uint64"); toml.put ("database_warmup_ratio", database_warmup_ratio, "Ratio of the database rate limit to use for the initial warmup.\ntype:uint64"); toml.put ("max_pull_count", max_pull_count, "Maximum number of requested blocks for bootstrap request.\ntype:uint64"); diff --git a/nano/node/bootstrap/bootstrap_config.hpp b/nano/node/bootstrap/bootstrap_config.hpp index d5e6b46d48..22284e6d55 100644 --- a/nano/node/bootstrap/bootstrap_config.hpp +++ b/nano/node/bootstrap/bootstrap_config.hpp @@ -48,7 +48,8 @@ class bootstrap_config final // Maximum number of un-responded requests per channel, should be lower or equal to bootstrap server max queue size std::size_t channel_limit{ 16 }; - std::size_t database_rate_limit{ 256 }; + std::size_t rate_limit{ 500 }; + std::size_t database_rate_limit{ 250 }; std::size_t frontier_rate_limit{ 8 }; std::size_t database_warmup_ratio{ 10 }; std::size_t max_pull_count{ nano::bootstrap_server::max_blocks }; diff --git a/nano/node/bootstrap/bootstrap_service.cpp b/nano/node/bootstrap/bootstrap_service.cpp index 5c045b76b2..4b93c0d445 100644 --- a/nano/node/bootstrap/bootstrap_service.cpp +++ b/nano/node/bootstrap/bootstrap_service.cpp @@ -30,6 +30,7 @@ nano::bootstrap_service::bootstrap_service (nano::node_config const & node_confi frontiers{ config.frontier_scan, stats }, throttle{ compute_throttle_size () }, scoring{ config, node_config_a.network_params.network }, + limiter{ config.rate_limit }, database_limiter{ config.database_rate_limit }, frontiers_limiter{ config.frontier_rate_limit }, workers{ 1, nano::thread_role::name::bootstrap_worker } @@ -301,14 +302,6 @@ void nano::bootstrap_service::wait (std::function const & predicate) co } } -void nano::bootstrap_service::wait_tags () const -{ - wait ([this] () { - debug_assert (!mutex.try_lock ()); - return tags.size () < config.max_requests; - }); -} - void nano::bootstrap_service::wait_blockprocessor () const { wait ([this] () { @@ -318,9 +311,19 @@ void nano::bootstrap_service::wait_blockprocessor () const std::shared_ptr nano::bootstrap_service::wait_channel () { + // Limit the number of in-flight requests + wait ([this] () { + return tags.size () < config.max_requests; + }); + + // Wait until more requests can be sent + wait ([this] () { + return limiter.should_pass (1); + }); + + // Wait until a channel is available std::shared_ptr channel; wait ([this, &channel] () { - debug_assert (!mutex.try_lock ()); channel = scoring.channel (); return channel != nullptr; // Wait until a channel is available }); @@ -528,7 +531,6 @@ bool nano::bootstrap_service::request_frontiers (nano::account start, std::share void nano::bootstrap_service::run_one_priority () { - wait_tags (); wait_blockprocessor (); auto channel = wait_channel (); if (!channel) @@ -559,7 +561,6 @@ void nano::bootstrap_service::run_priorities () void nano::bootstrap_service::run_one_database (bool should_throttle) { - wait_tags (); wait_blockprocessor (); auto channel = wait_channel (); if (!channel) @@ -590,8 +591,7 @@ void nano::bootstrap_service::run_database () void nano::bootstrap_service::run_one_blocking () { - wait_tags (); - wait_blockprocessor (); + // No need to wait for blockprocessor, as we are not processing blocks auto channel = wait_channel (); if (!channel) { @@ -619,6 +619,7 @@ void nano::bootstrap_service::run_dependencies () void nano::bootstrap_service::run_one_frontier () { + // No need to wait for blockprocessor, as we are not processing blocks wait ([this] () { return !accounts.priority_half_full (); }); @@ -628,7 +629,6 @@ void nano::bootstrap_service::run_one_frontier () wait ([this] () { return workers.queued_tasks () < config.frontier_scan.max_pending; }); - wait_tags (); auto channel = wait_channel (); if (!channel) { diff --git a/nano/node/bootstrap/bootstrap_service.hpp b/nano/node/bootstrap/bootstrap_service.hpp index f73e49a63c..8cd7ac651e 100644 --- a/nano/node/bootstrap/bootstrap_service.hpp +++ b/nano/node/bootstrap/bootstrap_service.hpp @@ -113,8 +113,6 @@ class bootstrap_service /* Waits for a condition to be satisfied with incremental backoff */ void wait (std::function const & predicate) const; - /* Avoid too many in-flight requests */ - void wait_tags () const; /* Ensure there is enough space in blockprocessor for queuing new blocks */ void wait_blockprocessor () const; /* Waits for a channel that is not full */ @@ -191,6 +189,8 @@ class bootstrap_service // clang-format on ordered_tags tags; + // Rate limiter for all types of requests + nano::rate_limiter limiter; // Requests for accounts from database have much lower hitrate and could introduce strain on the network // A separate (lower) limiter ensures that we always reserve resources for querying accounts from priority queue nano::rate_limiter database_limiter;