diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 845513c36ce8d09a4cd01971d125e2a0c0e6f5b6..f5dbd4e413058799ce9dbcab5f92851c897dce32 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -1683,10 +1683,9 @@ void chain_controller::update_usage( transaction_metadata& meta, uint32_t act_us } } - _db.modify( dgpo, [&]( auto& props ) { - props.average_block_acts.add_usage( act_usage, head_time ); + _db.modify( state, [&]( rate_limiting_state_object& rls ) { + rls.average_block_cpu_usage.add_usage( cpu_usage, head_time ); }); - } const apply_handler* chain_controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const diff --git a/libraries/chain/global_property_object.cpp b/libraries/chain/global_property_object.cpp index 8d3db8a79df149e7e95b3d05b3b5ffa651055b3d..bc5270d27727c35ff37ba5f22e585553ee82d34d 100644 --- a/libraries/chain/global_property_object.cpp +++ b/libraries/chain/global_property_object.cpp @@ -2,38 +2,4 @@ namespace eosio { namespace chain { -void dynamic_global_property_object::update_virtual_net_bandwidth( const chain_config& cfg ) { - - if( average_block_size.average()> (cfg.target_block_size * config::rate_limiting_precision) ) { - virtual_net_bandwidth *= 99; - virtual_net_bandwidth /= 100; - wlog( "reducing virtual net bandwidth by 1%, ${vnb}", ("vnb", virtual_net_bandwidth) ); - wdump((average_block_size.average())(cfg.target_block_size*config::rate_limiting_precision)); - } else { - virtual_net_bandwidth *= 1000; - virtual_net_bandwidth /= 999; -// ilog( "increasing virtual net bandwidth by .1%, ${vnb}", ("vnb", virtual_net_bandwidth) ); - } - - auto min = (cfg.max_block_size * config::blocksize_average_window_ms) / config::block_interval_ms; - - if( virtual_net_bandwidth < min ) virtual_net_bandwidth = min; - if( virtual_net_bandwidth > min * 1000 ) virtual_net_bandwidth = min * 1000; -} - -void dynamic_global_property_object::update_virtual_act_bandwidth( const chain_config& cfg ) { - if( average_block_acts.average() > (cfg.target_block_acts * config::rate_limiting_precision) ) { - virtual_act_bandwidth *= 99; - virtual_act_bandwidth /= 100; - } else { - virtual_act_bandwidth *= 1000; - virtual_act_bandwidth /= 999; - } - - auto min = (cfg.max_block_acts * config::blocksize_average_window_ms) / config::block_interval_ms; - - if( virtual_act_bandwidth < min ) virtual_act_bandwidth = min; - if( virtual_act_bandwidth > min * 1000 ) virtual_act_bandwidth = min * 1000; -} - } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/rate_limiting.hpp b/libraries/chain/include/eosio/chain/rate_limiting.hpp index f4536c5ec3fc954b7ca447a3aa41db8bea4a3295..bbc394b7906f44eb41ddf522281894ea62d9f9b1 100644 --- a/libraries/chain/include/eosio/chain/rate_limiting.hpp +++ b/libraries/chain/include/eosio/chain/rate_limiting.hpp @@ -7,38 +7,57 @@ namespace eosio { namespace chain { + template + struct ratio { + T numerator; + T denominator; + }; + + template + ratio make_ratio(T n, T d) { + return ratio{n, d}; + } + + template + T operator* (T value, const ratio& r) { + return (value * r.numerator) / r.denominator; + } + /** * This class accumulates an average value taking periodic inputs. * - * The average value returns to 0 after WindowMS without any updates and - * decays linerally if updates occur in the middle of a window before adding - * the new value. - * * The value stored is Precision times the sum of the inputs. */ - template + template struct average_accumulator { - time_point last_update; - uint64_t value = 0; + average_accumulator(uint32_t window_blocks) + : window_blocks(window_blocks) + , last_head_block_num() + , value(0) + { + } + + uint32_t window_blocks; + uint32_t last_head_block_num; + uint64_t value; /** * return the average value in rate_limiting_precision */ - uint64_t average()const { return value / WindowMs; } + uint64_t average()const { return value / window_blocks; } - void add_usage( uint64_t units, time_point now ) + void add_usage( uint64_t units, uint32_t head_block_num ) { - if( now != last_update ) { - auto elapsed = now - last_update; - if( elapsed > fc::milliseconds(WindowMs) ) { - value = 0; - } else if( elapsed > fc::days(0) ) { - value *= (fc::milliseconds(WindowMs) - elapsed).count(); - value /= fc::milliseconds(WindowMs).count(); - } - last_update = now; + if( head_block_num != last_head_block_num ) { + const auto decay = make_ratio( + (uint64_t) window_blocks - 1, + (uint64_t) window_blocks + ); + + value = value * decay; } + value += units * Precision; } }; @@ -94,6 +113,29 @@ namespace eosio { namespace chain { > >; + struct elastic_limit_parameters { + uint64_t target; // the desired usage + uint64_t max; // the maximum usage + uint32_t periods; // the number of aggregation periods that contribute to the average usage + + uint32_t max_multiplier; // the multiplier by which virtual space can oversell usage when uncongested + ratio contract_rate; // the rate at which a congested resource contracts its limit + ratio expand_rate; // the rate at which an uncongested resource expands its limits + }; + + class resource_limits_config_object : public chainbase::object { + OBJECT_CTOR(resource_limits_config_object); + id_type id; + + uint32_t base_per_transaction_net_usage; + uint32_t base_per_transaction_cpu_usage; + + uint32_t per_signature_cpu_usage; + + elastic_limit_parameters cpu_limit_parameters; + elastic_limit_parameters net_limit_parameters; + }; + class resource_limits_state_object : public chainbase::object { OBJECT_CTOR(resource_limits_state_object); @@ -101,16 +143,16 @@ namespace eosio { namespace chain { * Track the average blocksize over the past 60 seconds and use it to adjust the * reserve ratio for bandwidth rate limiting calclations. */ - average_accumulator average_block_size; + average_accumulator average_block_net_usage; /** * Track the average actions per block over the past 60 seconds and use it to * adjust hte reserve ration for action rate limiting calculations */ - average_accumulator average_block_acts; + average_accumulator average_block_cpu_usage; - void update_virtual_net_bandwidth( const chain_config& cfg ); - void update_virtual_act_bandwidth( const chain_config& cfg ); + void update_virtual_net_limit( const resource_limits_config_object& cfg ); + void update_virtual_cpu_limit( const resource_limits_config_object& cfg ); uint64_t total_net_weight = 0; uint64_t total_cpu_weight = 0; @@ -146,16 +188,6 @@ namespace eosio { namespace chain { }; - class resource_limits_config_object : public chainbase::object { - OBJECT_CTOR(resource_limits_config_object); - id_type id; - - uint32_t base_per_transaction_net_usage; - uint32_t base_per_transaction_cpu_usage; - - uint32_t per_signature_cpu_usage; - }; - using resource_limits_config_index = chainbase::shared_multi_index_container< resource_limits_config_object, indexed_by< diff --git a/libraries/chain/rate_limiting.cpp b/libraries/chain/rate_limiting.cpp index a9280fa09022eb7b63227e4b2f27fb5223079229..0c5b5fb97d7872dd69846bf2b9b162de268da7f3 100644 --- a/libraries/chain/rate_limiting.cpp +++ b/libraries/chain/rate_limiting.cpp @@ -1,5 +1,30 @@ #include +#include + namespace eosio { namespace chain { + +static uint64_t update_elastic_limit(uint64_t current_limit, uint64_t average_usage_ex, const elastic_limit_parameters& params) { + uint64_t result = current_limit; + if (average_usage_ex > (params.target * config::rate_limiting_precision) ) { + result = result * params.contract_rate; + } else { + result = result * params.expand_rate; + } + + uint64_t min = params.max * params.periods; + return std::min(std::max(result, min), min * params.max_multiplier)); +} + +void resource_limits_state_object::update_virtual_cpu_limit( const resource_limits_config_object& cfg ) { + virtual_cpu_limit = update_elastic_limit(virtual_cpu_limit, average_block_cpu_usage.average(), cfg.cpu_limit_parameters); +} + +void resource_limits_state_object::update_virtual_net_limit( const resource_limits_config_object& cfg ) { + virtual_net_limit = update_elastic_limit(virtual_net_limit, average_block_net_usage.average(), cfg.net_limit_parameters); +} + + + } } /// eosio::chain