美文网首页
EOS区块生产schedule_production_loop函

EOS区块生产schedule_production_loop函

作者: vergil6 | 来源:发表于2018-11-12 10:34 被阅读41次

    producer_plugin插件中的区块生产循环主函数schedule_production_loop,该函数的最主要目的是每次调用它会开启一个pending block,pending block打包交易为生产下一个block提供上下文,如果是当前生产者那么pending block最终成为block,反之则会在接收到一个block之后抛弃当前pending block,下面给出schedule_production_loop函数并提供详细注释

    void producer_plugin_impl::schedule_production_loop() {
       chain::controller& chain = app().get_plugin<chain_plugin>().chain();
       // 每次调用 schedule_production_loop 之前设置的_timer会被取消
       _timer.cancel();
       std::weak_ptr<producer_plugin_impl> weak_this = shared_from_this();
    
       bool last_block;
       // 开启一个pending block并执行unapplied_transactions和scheduled_transactions
       auto result = start_block(last_block);
    
      // 执行unapplied_transactions和scheduled_transactions过程中遇到的错误
       if (result == start_block_result::failed) {
          elog("Failed to start a pending block, will try again later");
          _timer.expires_from_now( boost::posix_time::microseconds( config::block_interval_us  / 10 ));
    
          // we failed to start a block, so try again later?
          _timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
             auto self = weak_this.lock();
             if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
                self->schedule_production_loop();
             }
          });
       } 
       // 从start_block可以看到 now - chain.head_block_time() > 5 才会返回start_block_result::waiting,这种情况一般发生在区块没有
       // 同步到最新,这种情况下只能等待直到区块同步到最新,注意这个时候 pending为空
       else if (result == start_block_result::waiting) {
          // nothing to do until more blocks arrive
          elog("nothing to do until more blocks arrive");
    
       } 
       // 轮到当前节点生产
       else if (_pending_block_mode == pending_block_mode::producing) {
    
          // we succeeded but block may be exhausted
          static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
          if (result == start_block_result::succeeded) {
             // ship this block off no later than its deadline
              //std::cout<<"expires_at: "<<epoch + boost::posix_time::microseconds(chain.pending_block_time().time_since_epoch().count() + (last_block ? _last_block_time_offset_us : _produce_time_offset_us))<<std::endl;
             _timer.expires_at(epoch + boost::posix_time::microseconds(chain.pending_block_time().time_since_epoch().count() + (last_block ? _last_block_time_offset_us : _produce_time_offset_us)));
             fc_dlog(_log, "Scheduling Block Production on Normal Block #${num} for ${time}", ("num", chain.pending_block_state()->block_num)("time",chain.pending_block_time()));
          } 
          // start_block_result::exhausted,跳过当前生产时间进入下一个块时间
          else {
             auto expect_time = chain.pending_block_time() - fc::microseconds(config::block_interval_us);
             // ship this block off up to 1 block time earlier or immediately
             if (fc::time_point::now() >= expect_time) {
                _timer.expires_from_now( boost::posix_time::microseconds( 0 ));
             } else {
                _timer.expires_at(epoch + boost::posix_time::microseconds(expect_time.time_since_epoch().count()));
             }
             fc_dlog(_log, "Scheduling Block Production on Exhausted Block #${num} immediately", ("num", chain.pending_block_state()->block_num));
          }
    
          _timer.async_wait([&chain,weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
             auto self = weak_this.lock();
             if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
               // 醒来之后 maybe_produce_block
                auto res = self->maybe_produce_block();
                fc_dlog(_log, "Producing Block #${num} returned: ${res}", ("num", chain.pending_block_state()->block_num)("res", res) );
             }
          });
       } 
       // 没轮到当前节点生产,但当前节点是生产者
       else if (_pending_block_mode == pending_block_mode::speculating && !_producers.empty() && !production_disabled_by_policy()){
          // if we have any producers then we should at least set a timer for our next available slot
          optional<fc::time_point> wake_up_time;
          for (const auto&p: _producers) {
            // 计算下一个轮到当前节点生产的时间
             auto next_producer_block_time = calculate_next_block_time(p);
             if (next_producer_block_time) {
               // 这里减去了0.5s是对的,这样醒来的时候正好进入上面那个条件_pending_block_mode == pending_block_mode::producing,否则
               // 会错误一次块生产机会
                auto producer_wake_up_time = *next_producer_block_time - fc::microseconds(config::block_interval_us);
                if (wake_up_time) {
                   // wake up with a full block interval to the deadline
                   wake_up_time = std::min<fc::time_point>(*wake_up_time, producer_wake_up_time);
                } else {
                   wake_up_time = producer_wake_up_time;
                }
             }
          }
    
          if (wake_up_time) {
             fc_dlog(_log, "Specualtive Block Created; Scheduling Speculative/Production Change at ${time}", ("time", wake_up_time));
             static const boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
             _timer.expires_at(epoch + boost::posix_time::microseconds(wake_up_time->time_since_epoch().count()));
             _timer.async_wait([weak_this,cid=++_timer_corelation_id](const boost::system::error_code& ec) {
                auto self = weak_this.lock();
                if (self && ec != boost::asio::error::operation_aborted && cid == self->_timer_corelation_id) {
                   self->schedule_production_loop();
                }
             });
          } else {
             fc_dlog(_log, "Speculative Block Created; Not Scheduling Speculative/Production, no local producers had valid wake up times");
          }
       } 
       // 当前节点为非生产节点
       else {
          fc_dlog(_log, "Speculative Block Created");
       }
    }
    

    该函数其实最重要的2个调用点:
    1)on_incoming_block 接收到网络中其他生产者生产的一个block之后,抛弃当前pending block,把新接收到的block当成新的block,最终调用该函数重新开启一个pending block
    2)schedule_production_loop自身的定时器,一般发生在当前节点轮到生产block的时候,每隔0.5s醒来commit block之后会调用schedule_production_loop

    相关文章

      网友评论

          本文标题:EOS区块生产schedule_production_loop函

          本文链接:https://www.haomeiwen.com/subject/taeqfqtx.html