EOS区块生产schedule_production_loop函

2018-11-12  本文已影响41人  vergil6

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

上一篇 下一篇

猜你喜欢

热点阅读