1. 程式人生 > >eos cpu_limit

eos cpu_limit

Eos中,通過抵押token的方式,提供cpu運算時間以供使用者完成交易。
net資源計算方式與cpu類似。
本文旨在理清變數間關係,故略過大部分中間變數,僅記錄關聯度較大的部分。

在eos測試中,用賬號進行高頻的transfer(轉賬)操作,出現了以下報錯。
所以決定做一份關於cpu限制的筆記。

3080004 tx_cpu_usage_exceeded: Transaction exceeded the current CPU usage limit imposed on the transaction
billed CPU time (916 us) is greater than the maximum billable CPU time for the transaction (372 us)
{"billed":916,"billable":372}
thread-0 transaction_context.cpp:361 validate_cpu_usage_to_bill

交易CPU驗證三步曲

主要由transaction_context承擔驗證工作。

  • transaction_context::init
    objective_duration_limit = 等於以下四者最小值。
    _deadline = start + objective_duration_limit。

    • config::max_transaction_cpu_usage:config.hpp
    • trx.max_cpu_usage_ms:nodeos --max-cpu-usage-ms
    • resource_limits_manager::get_block_cpu_limit()
      :動態變化_db.get<resource_limits_config_object>().cpu_limit_parameters.max - _db.get<resource_limits_state_object>().pending_cpu_usage
    • resource_limits_manager::get_account_cpu_limit + leeway:動態變化,使用者視窗期可用總值-已使用值。
  • transaction_context::exec
    非延遲類交易立即執行,延遲類交易計算掛起費用(網路、記憶體),其中記憶體費用由交易發起者支付。

  • transaction_context::finalize
    驗證交易執行結果,計算費用。

    cpu賬單需滿足以下斷言:
    billed_us >= cfg.min_transaction_cpu_usage:config.hpp
    billed_us <= objective_duration_limit.count():上面的objective_duration_limit

由此看來,節點間硬體配置差距將導致結果不唯一。

使用者CPU資源使用計算

上面我們大概瞭解了什麼因素會影響cpu_limit的判定,現在要開始弄明白使用者cpu的計算方式。

使用cleos get account我們可以看到cpu相關引數如下:

cpu bandwidth:
staked: 4448951.0162 SYS (total stake delegated from account to self)
delegated: 0.0000 SYS (total staked delegated to account from others)
used: 47.11 ms
available: 85.42 hr
limit: 85.42 hr

// 列印程式碼如下:
std::cout << indent << std::left << std::setw(11) << "used:"      << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.used ) << "\n";
std::cout << indent << std::left << std::setw(11) << "available:" << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.available ) << "\n";
std::cout << indent << std::left << std::setw(11) << "limit:"     << std::right << std::setw(18) << to_pretty_time( res.cpu_limit.max ) << "\n";

其中res.cpu_limit的值源自get_account_cpu_limit_ex,最終與_dbresource_limits*欄位相關聯。
那麼我們需要弄懂相關欄位的含義。

  • account_limit

    • _db.get<resource_limits_object, by_owner>(boost::make_tuple(bool pending, account_name account).cpu_weight
      使用者抵押貨幣後理論cpu時間最大值。
      • pending:false時,記錄上個塊結束時的舊值。
      • pending:true時,記錄當前塊發生交易後的新值。
        當用戶產生交易後,在controller_impl::finalize_block()中更新:
        _db<resource_limits_object, by_owner>(false, account) = _db<resource_limits_object, by_owner>(true, account)
  • account_usage

    • _db.get<resource_usage_object, by_owner>
      視窗期內使用者使用的資源,在add_transaction_usage內增加。
  • block_limit

    • _db.get<resource_limits_state_object>:整個塊中使用者計算資源消耗累計,累計方式同account_usage,根據下文config_object的值決定virtual_net_limit,動態調節限制。
      可以參考virtual_net_limit程式碼註釋:

      /**
      * The virtual number of bytes that would be consumed over blocksize_average_window_ms
      * if all blocks were at their maximum virtual size. This is virtual because the
      * real maximum block is less, this virtual number is only used for rate limiting users.
      *
      * It's lowest possible value is max_block_size * blocksize_average_window_ms / block_interval
      * It's highest possible value is 1000 times its lowest possible value
      *
      * This means that the most an account can consume during idle periods is 1000x the bandwidth
      * it is gauranteed under congestion.
      *
      * Increases when average_block_size < target_block_size, decreases when
      * average_block_size > target_block_size, with a cap at 1000x max_block_size
      * and a floor at max_block_size;
      **/

  • _db.get<resource_limits_config_object>:記錄著整個硬體資源限制的配置,目前來看似乎是定值。
class resource_limits_config_object : public chainbase::object<resource_limits_config_object_type, resource_limits_config_object> {
    OBJECT_CTOR(resource_limits_config_object);
    id_type id;

    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

    void validate()const; // throws if the parameters do not satisfy basic sanity checks
    };

    elastic_limit_parameters cpu_limit_parameters = {EOS_PERCENT(config::default_max_block_cpu_usage, config::default_target_block_cpu_usage_pct), config::default_max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}};
    elastic_limit_parameters net_limit_parameters = {EOS_PERCENT(config::default_max_block_net_usage, config::default_target_block_net_usage_pct), config::default_max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, 1000, {99, 100}, {1000, 999}};

    // window_size = account_cpu_usage_average_window
    uint32_t account_cpu_usage_average_window = config::account_cpu_usage_average_window_ms / config::block_interval_ms;
    uint32_t account_net_usage_average_window = config::account_net_usage_average_window_ms / config::block_interval_ms;
}
// 在controller_impl::finalize_block中執行更新
uint64_t CPU_TARGET = EOS_PERCENT(chain_config.max_block_cpu_usage, chain_config.target_block_cpu_usage_pct);
resource_limits.set_block_parameters(
    { CPU_TARGET, chain_config.max_block_cpu_usage, config::block_cpu_usage_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}},
    {EOS_PERCENT(chain_config.max_block_net_usage, chain_config.target_block_net_usage_pct), chain_config.max_block_net_usage, config::block_size_average_window_ms / config::block_interval_ms, max_virtual_mult, {99, 100}, {1000, 999}}
);

轉自:https://www.jianshu.com/p/e3ea61e7ccd9