Scale bootstrap connections based on load. (#552)

This commit is contained in:
Luke Alonso 2018-01-29 06:04:27 -08:00 committed by Russel Waters
commit 551412128c
5 changed files with 56 additions and 8 deletions

View file

@ -1459,3 +1459,24 @@ TEST (node, balance_observer)
ASSERT_GT (200, iterations);
}
}
TEST (node, bootstrap_connection_scaling)
{
rai::system system (24000, 1);
auto & node1 (*system.nodes[0]);
node1.bootstrap_initiator.bootstrap ();
auto & attempt = node1.bootstrap_initiator.attempt;
ASSERT_EQ (34, attempt->target_connections (25000));
ASSERT_EQ (4, attempt->target_connections (0));
ASSERT_EQ (64, attempt->target_connections (50000));
ASSERT_EQ (64, attempt->target_connections (10000000000));
node1.config.bootstrap_connections = 128;
ASSERT_EQ (64, attempt->target_connections (0));
ASSERT_EQ (64, attempt->target_connections (50000));
node1.config.bootstrap_connections_max = 256;
ASSERT_EQ (128, attempt->target_connections (0));
ASSERT_EQ (256, attempt->target_connections (50000));
node1.config.bootstrap_connections_max = 0;
ASSERT_EQ (1, attempt->target_connections (0));
ASSERT_EQ (1, attempt->target_connections (50000));
}

View file

@ -5,9 +5,14 @@
#include <boost/log/trivial.hpp>
// Updated 1-27-18
constexpr double bootstrap_connection_scale_target = 50000.0;
constexpr double bootstrap_connection_warmup_time = 5.0;
constexpr double bootstrap_minimum_block_rate = 10.0;
constexpr double bootstrap_minimum_termination_time = 30.0;
constexpr unsigned bootstrap_max_new_connections = 10;
constexpr unsigned bootstrap_peer_frontier_minimum = rai::rai_network == rai::rai_networks::rai_live_network ? 339000 : 0;
rai::block_synchronization::block_synchronization (boost::log::sources::logger_mt & log_a) :
log (log_a)
{
@ -971,12 +976,26 @@ struct block_rate_cmp
}
};
unsigned rai::bootstrap_attempt::target_connections (size_t pulls_remaining)
{
if (node->config.bootstrap_connections >= node->config.bootstrap_connections_max) {
return std::max(1U, node->config.bootstrap_connections_max);
}
// Only scale up to bootstrap_connections_max for large pulls.
double step = std::min (1.0, std::max (0.0, (double)pulls_remaining / bootstrap_connection_scale_target));
double target = (double)node->config.bootstrap_connections + (double)(node->config.bootstrap_connections_max - node->config.bootstrap_connections) * step;
return std::max(1U, (unsigned)(target + 0.5f));
}
void rai::bootstrap_attempt::populate_connections ()
{
double rate_sum = 0.0;
size_t num_pulls = 0;
std::priority_queue<std::shared_ptr<rai::bootstrap_client>, std::vector<std::shared_ptr<rai::bootstrap_client>>, block_rate_cmp> sorted_connections;
{
std::unique_lock<std::mutex> lock (mutex);
num_pulls = pulls.size ();
for (auto & c : clients)
{
if (auto client = c.lock ())
@ -984,13 +1003,13 @@ void rai::bootstrap_attempt::populate_connections ()
double elapsed = client->elapsed_seconds ();
auto rate = client->block_rate ();
rate_sum += rate;
if (client->elapsed_seconds () > 5.0 && client->block_count > 0)
if (client->elapsed_seconds () > bootstrap_connection_warmup_time && client->block_count > 0)
{
sorted_connections.push (client);
}
// Force-stop the slowest peers, since they can take the whole bootstrap hostage by dribbling out blocks on the last remaining pull.
// This is ~1.5kilobits/sec.
if (elapsed > 30.0 && rate < 10.0)
if (elapsed > bootstrap_minimum_termination_time && rate < bootstrap_minimum_block_rate)
{
client->stop (true);
}
@ -998,12 +1017,14 @@ void rai::bootstrap_attempt::populate_connections ()
}
}
auto target = target_connections (num_pulls);
// We only want to drop slow peers when more than 2/3 are active. 2/3 because 1/2 is too aggressive, and 100% rarely happens.
// Probably needs more tuning.
if (sorted_connections.size () >= (node->config.bootstrap_connections * 2) / 3 && node->config.bootstrap_connections >= 4)
if (sorted_connections.size () >= (target * 2) / 3 && target >= 4)
{
// 4 -> 1, 8 -> 2, 16 -> 4, arbitrary, but seems to work well.
auto drop = (int)roundf (sqrtf ((float)node->config.bootstrap_connections - 2.0f));
auto drop = (int)roundf (sqrtf ((float)target - 2.0f));
for (int i = 0; i < drop; i++)
{
auto client = sorted_connections.top ();
@ -1018,9 +1039,9 @@ void rai::bootstrap_attempt::populate_connections ()
BOOST_LOG (node->log) << boost::str (boost::format ("Bulk pull connections: %1%, rate: %2% blocks/sec, remaining account pulls: %3%, total blocks: %4%") % connections.load () % (int)rate_sum % pulls.size () % (int)total_blocks.load ());
}
if (connections < node->config.bootstrap_connections)
if (connections < target)
{
auto delta = std::min ((node->config.bootstrap_connections - connections) * 2, 10U);
auto delta = std::min ((target - connections) * 2, bootstrap_max_new_connections);
// TODO - tune this better
// Not many peers respond, need to try to make more connections than we need.
for (int i = 0; i < delta; i++)

View file

@ -88,6 +88,7 @@ public:
void add_pull (rai::pull_info const &);
bool still_pulling ();
void process_fork (MDB_txn *, std::shared_ptr<rai::block>);
unsigned target_connections (size_t pulls_remaining);
std::deque<std::weak_ptr<rai::bootstrap_client>> clients;
std::weak_ptr<rai::bootstrap_client> connection_frontier_request;
std::weak_ptr<rai::frontier_req_client> frontiers;

View file

@ -750,7 +750,8 @@ password_fanout (1024),
io_threads (std::max<unsigned> (4, std::thread::hardware_concurrency ())),
work_threads (std::max<unsigned> (4, std::thread::hardware_concurrency ())),
enable_voting (true),
bootstrap_connections (16),
bootstrap_connections (4),
bootstrap_connections_max (64),
callback_port (0),
lmdb_max_dbs (128)
{
@ -822,6 +823,7 @@ void rai::node_config::serialize_json (boost::property_tree::ptree & tree_a) con
tree_a.put ("work_threads", std::to_string (work_threads));
tree_a.put ("enable_voting", enable_voting);
tree_a.put ("bootstrap_connections", bootstrap_connections);
tree_a.put ("bootstrap_connections_max", bootstrap_connections_max);
tree_a.put ("callback_address", callback_address);
tree_a.put ("callback_port", std::to_string (callback_port));
tree_a.put ("callback_target", callback_target);
@ -966,6 +968,7 @@ bool rai::node_config::deserialize_json (bool & upgraded_a, boost::property_tree
auto work_threads_l (tree_a.get<std::string> ("work_threads"));
enable_voting = tree_a.get<bool> ("enable_voting");
auto bootstrap_connections_l (tree_a.get<std::string> ("bootstrap_connections"));
auto bootstrap_connections_max_l (tree_a.get<std::string> ("bootstrap_connections_max"));
callback_address = tree_a.get<std::string> ("callback_address");
auto callback_port_l (tree_a.get<std::string> ("callback_port"));
callback_target = tree_a.get<std::string> ("callback_target");
@ -979,6 +982,7 @@ bool rai::node_config::deserialize_json (bool & upgraded_a, boost::property_tree
io_threads = std::stoul (io_threads_l);
work_threads = std::stoul (work_threads_l);
bootstrap_connections = std::stoul (bootstrap_connections_l);
bootstrap_connections_max = std::stoul (bootstrap_connections_max_l);
lmdb_max_dbs = std::stoi (lmdb_max_dbs_l);
result |= peering_port > std::numeric_limits<uint16_t>::max ();
result |= logging.deserialize_json (upgraded_a, logging_l);

View file

@ -400,6 +400,7 @@ public:
unsigned work_threads;
bool enable_voting;
unsigned bootstrap_connections;
unsigned bootstrap_connections_max;
std::string callback_address;
uint16_t callback_port;
std::string callback_target;