Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per traffic type stats #4785

Merged
merged 4 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nano/core_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_executable(
processor_service.cpp
random.cpp
random_pool.cpp
rate_limiting.cpp
rep_crawler.cpp
receivable.cpp
peer_history.cpp
Expand Down
113 changes: 113 additions & 0 deletions nano/core_test/rate_limiting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include <nano/lib/rate_limiting.hpp>
#include <nano/lib/utility.hpp>

#include <gtest/gtest.h>

#include <fstream>
#include <future>

using namespace std::chrono_literals;

TEST (rate, basic)
{
nano::rate::token_bucket bucket (10, 10);

// Initial burst
ASSERT_TRUE (bucket.try_consume (10));
ASSERT_FALSE (bucket.try_consume (10));

// With a fill rate of 10 tokens/sec, await 1/3 sec and get 3 tokens
std::this_thread::sleep_for (300ms);
ASSERT_TRUE (bucket.try_consume (3));
ASSERT_FALSE (bucket.try_consume (10));

// Allow time for the bucket to completely refill and do a full burst
std::this_thread::sleep_for (1s);
ASSERT_TRUE (bucket.try_consume (10));
ASSERT_EQ (bucket.largest_burst (), 10);
}

TEST (rate, network)
{
// For the purpose of the test, one token represents 1MB instead of one byte.
// Allow for 10 mb/s bursts (max bucket size), 5 mb/s long term rate
nano::rate::token_bucket bucket (10, 5);

// Initial burst of 10 mb/s over two calls
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 5);
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 10);
ASSERT_FALSE (bucket.try_consume (5));

// After 200 ms, the 5 mb/s fillrate means we have 1 mb available
std::this_thread::sleep_for (200ms);
ASSERT_TRUE (bucket.try_consume (1));
ASSERT_FALSE (bucket.try_consume (1));
}

TEST (rate, reset)
{
nano::rate::token_bucket bucket (0, 0);

// consume lots of tokens, buckets should be unlimited
ASSERT_TRUE (bucket.try_consume (1000000));
ASSERT_TRUE (bucket.try_consume (1000000));

// set bucket to be limited
bucket.reset (1000, 1000);
ASSERT_FALSE (bucket.try_consume (1001));
ASSERT_TRUE (bucket.try_consume (1000));
ASSERT_FALSE (bucket.try_consume (1000));
std::this_thread::sleep_for (2ms);
ASSERT_TRUE (bucket.try_consume (2));

// reduce the limit
bucket.reset (100, 100 * 1000);
ASSERT_FALSE (bucket.try_consume (101));
ASSERT_TRUE (bucket.try_consume (100));
std::this_thread::sleep_for (1ms);
ASSERT_TRUE (bucket.try_consume (100));

// increase the limit
bucket.reset (2000, 1);
ASSERT_FALSE (bucket.try_consume (2001));
ASSERT_TRUE (bucket.try_consume (2000));

// back to unlimited
bucket.reset (0, 0);
ASSERT_TRUE (bucket.try_consume (1000000));
ASSERT_TRUE (bucket.try_consume (1000000));
}

TEST (rate, unlimited)
{
nano::rate::token_bucket bucket (0, 0);
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 5);
ASSERT_TRUE (bucket.try_consume (static_cast<size_t> (1e9)));
ASSERT_EQ (bucket.largest_burst (), static_cast<size_t> (1e9));

// With unlimited tokens, consuming always succeed
ASSERT_TRUE (bucket.try_consume (static_cast<size_t> (1e9)));
ASSERT_EQ (bucket.largest_burst (), static_cast<size_t> (1e9));
}

TEST (rate, busy_spin)
{
// Bucket should refill at a rate of 1 token per second
nano::rate::token_bucket bucket (1, 1);

// Run a very tight loop for 5 seconds + a bit of wiggle room
int counter = 0;
for (auto start = std::chrono::steady_clock::now (), now = start; now < start + 5500ms; now = std::chrono::steady_clock::now ())
{
if (bucket.try_consume ())
{
++counter;
}
}

// Bucket starts fully refilled, therefore we see 1 additional request
ASSERT_EQ (counter, 6);
}
104 changes: 0 additions & 104 deletions nano/core_test/utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,110 +15,6 @@

using namespace std::chrono_literals;

TEST (rate, basic)
{
nano::rate::token_bucket bucket (10, 10);

// Initial burst
ASSERT_TRUE (bucket.try_consume (10));
ASSERT_FALSE (bucket.try_consume (10));

// With a fill rate of 10 tokens/sec, await 1/3 sec and get 3 tokens
std::this_thread::sleep_for (300ms);
ASSERT_TRUE (bucket.try_consume (3));
ASSERT_FALSE (bucket.try_consume (10));

// Allow time for the bucket to completely refill and do a full burst
std::this_thread::sleep_for (1s);
ASSERT_TRUE (bucket.try_consume (10));
ASSERT_EQ (bucket.largest_burst (), 10);
}

TEST (rate, network)
{
// For the purpose of the test, one token represents 1MB instead of one byte.
// Allow for 10 mb/s bursts (max bucket size), 5 mb/s long term rate
nano::rate::token_bucket bucket (10, 5);

// Initial burst of 10 mb/s over two calls
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 5);
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 10);
ASSERT_FALSE (bucket.try_consume (5));

// After 200 ms, the 5 mb/s fillrate means we have 1 mb available
std::this_thread::sleep_for (200ms);
ASSERT_TRUE (bucket.try_consume (1));
ASSERT_FALSE (bucket.try_consume (1));
}

TEST (rate, reset)
{
nano::rate::token_bucket bucket (0, 0);

// consume lots of tokens, buckets should be unlimited
ASSERT_TRUE (bucket.try_consume (1000000));
ASSERT_TRUE (bucket.try_consume (1000000));

// set bucket to be limited
bucket.reset (1000, 1000);
ASSERT_FALSE (bucket.try_consume (1001));
ASSERT_TRUE (bucket.try_consume (1000));
ASSERT_FALSE (bucket.try_consume (1000));
std::this_thread::sleep_for (2ms);
ASSERT_TRUE (bucket.try_consume (2));

// reduce the limit
bucket.reset (100, 100 * 1000);
ASSERT_FALSE (bucket.try_consume (101));
ASSERT_TRUE (bucket.try_consume (100));
std::this_thread::sleep_for (1ms);
ASSERT_TRUE (bucket.try_consume (100));

// increase the limit
bucket.reset (2000, 1);
ASSERT_FALSE (bucket.try_consume (2001));
ASSERT_TRUE (bucket.try_consume (2000));

// back to unlimited
bucket.reset (0, 0);
ASSERT_TRUE (bucket.try_consume (1000000));
ASSERT_TRUE (bucket.try_consume (1000000));
}

TEST (rate, unlimited)
{
nano::rate::token_bucket bucket (0, 0);
ASSERT_TRUE (bucket.try_consume (5));
ASSERT_EQ (bucket.largest_burst (), 5);
ASSERT_TRUE (bucket.try_consume (static_cast<size_t> (1e9)));
ASSERT_EQ (bucket.largest_burst (), static_cast<size_t> (1e9));

// With unlimited tokens, consuming always succeed
ASSERT_TRUE (bucket.try_consume (static_cast<size_t> (1e9)));
ASSERT_EQ (bucket.largest_burst (), static_cast<size_t> (1e9));
}

TEST (rate, busy_spin)
{
// Bucket should refill at a rate of 1 token per second
nano::rate::token_bucket bucket (1, 1);

// Run a very tight loop for 5 seconds + a bit of wiggle room
int counter = 0;
for (auto start = std::chrono::steady_clock::now (), now = start; now < start + std::chrono::milliseconds{ 5500 }; now = std::chrono::steady_clock::now ())
{
if (bucket.try_consume ())
{
++counter;
}
}

// Bucket starts fully refilled, therefore we see 1 additional request
ASSERT_EQ (counter, 6);
}

TEST (optional_ptr, basic)
{
struct valtype
Expand Down
21 changes: 16 additions & 5 deletions nano/lib/rate_limiting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ void nano::rate::token_bucket::refill ()
}
}

std::size_t nano::rate::token_bucket::largest_burst () const
{
return max_token_count - smallest_size;
}

void nano::rate::token_bucket::reset (std::size_t max_token_count_a, std::size_t refill_rate_a)
{
// A token count of 0 indicates unlimited capacity. We use 1e9 as
Expand All @@ -63,6 +58,16 @@ void nano::rate::token_bucket::reset (std::size_t max_token_count_a, std::size_t
last_refill = std::chrono::steady_clock::now ();
}

std::size_t nano::rate::token_bucket::largest_burst () const
{
return max_token_count - smallest_size;
}

std::size_t nano::rate::token_bucket::size () const
{
return current_size;
}

/*
* rate_limiter
*/
Expand All @@ -82,4 +87,10 @@ void nano::rate_limiter::reset (std::size_t limit_a, double burst_ratio_a)
{
nano::lock_guard<nano::mutex> guard{ mutex };
bucket.reset (static_cast<std::size_t> (limit_a * burst_ratio_a), limit_a);
}

std::size_t nano::rate_limiter::size () const
{
nano::lock_guard<nano::mutex> guard{ mutex };
return bucket.size ();
}
9 changes: 6 additions & 3 deletions nano/lib/rate_limiting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ class token_bucket
*/
bool try_consume (unsigned tokens_required = 1);

/** Returns the largest burst observed */
std::size_t largest_burst () const;

/** Update the max_token_count and/or refill_rate_a parameters */
void reset (std::size_t max_token_count, std::size_t refill_rate);

/** Returns the largest burst observed */
std::size_t largest_burst () const;
std::size_t size () const;

private:
void refill ();

Expand Down Expand Up @@ -71,6 +72,8 @@ class rate_limiter final
bool should_pass (std::size_t buffer_size);
void reset (std::size_t limit, double burst_ratio = 1.0);

std::size_t size () const;

private:
nano::rate::token_bucket bucket;
mutable nano::mutex mutex;
Expand Down
8 changes: 6 additions & 2 deletions nano/lib/stats_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ enum class type
_invalid = 0, // Default value, should not be used

test,
traffic_tcp,
error,
message,
block,
ledger,
rollback,
network,
tcp_server,
vote,
vote_processor,
vote_processor_tier,
Expand All @@ -31,11 +29,14 @@ enum class type
http_callback,
ipc,
tcp,
tcp_server,
tcp_channels,
tcp_channels_rejected,
tcp_channels_purge,
tcp_listener,
tcp_listener_rejected,
traffic_tcp,
traffic_tcp_type,
channel,
socket,
confirmation_height,
Expand Down Expand Up @@ -294,6 +295,9 @@ enum class detail
reachout_live,
reachout_cached,

// traffic
generic,

// tcp
tcp_write_drop,
tcp_write_no_socket_drop,
Expand Down
2 changes: 2 additions & 0 deletions nano/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ add_library(
transport/tcp_server.cpp
transport/tcp_socket.hpp
transport/tcp_socket.cpp
transport/traffic_type.hpp
transport/traffic_type.cpp
transport/transport.hpp
transport/transport.cpp
unchecked_map.cpp
Expand Down
8 changes: 8 additions & 0 deletions nano/node/bandwidth_limiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ void nano::bandwidth_limiter::reset (std::size_t limit, double burst_ratio, nano
limiter.reset (limit, burst_ratio);
}

nano::container_info nano::bandwidth_limiter::container_info () const
{
nano::container_info info;
info.put ("generic", limiter_generic.size ());
info.put ("bootstrap", limiter_bootstrap.size ());
return info;
}

/*
* bandwidth_limiter_config
*/
Expand Down
2 changes: 2 additions & 0 deletions nano/node/bandwidth_limiter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class bandwidth_limiter final
*/
void reset (std::size_t limit, double burst_ratio, nano::transport::traffic_type type = nano::transport::traffic_type::generic);

nano::container_info container_info () const;

private:
/**
* Returns reference to limiter corresponding to the limit type
Expand Down
1 change: 1 addition & 0 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,7 @@ nano::container_info nano::node::container_info () const
info.add ("local_block_broadcaster", local_block_broadcaster.container_info ());
info.add ("rep_tiers", rep_tiers.container_info ());
info.add ("message_processor", message_processor.container_info ());
info.add ("bandwidth", outbound_limiter.container_info ());
return info;
}

Expand Down
Loading
Loading