ceph state machine

該系列文章主要記錄閱讀理解ceph代碼時可能遇到的一些難點,可能跳躍比較大。如果有描述錯誤或任何疑問歡迎交流討論。

狀態機本身機制是比較好理解的,看狀態機代碼時只要理解幾個特殊的地方就很容易。

默認子狀態

狀態機的部分狀態有默認子狀態,從該狀態到其默認子狀態是不需要事件觸發的。比如:

struct Primary : boost::statechart::state< Primary, Started, Peering >, NamedState
 {
     Primary(my_context ctx);
     void exit();

    typedef boost::mpl::list <
       boost::statechart::custom_reaction< ActMap >,
           boost::statechart::custom_reaction< MNotifyRec >,
           boost::statechart::transition< NeedActingChange, WaitActingChange >
     > reactions;
     boost::statechart::result react(const ActMap&);
     boost::statechart::result react(const MNotifyRec&);      
};               

這里定義了Primary狀態,其父狀態是Started,默認子狀態是Peering。因而從Primary進入Peering狀態不需要任何事件觸發。

狀態跳轉

狀態機是由事件驅動的,定義事件的響應方法有2種方式。

  • 注冊事件處理函數,比如上面Primary在處理ActMap事件就注冊了boost::statechart::result react(const ActMap&)函數。
  • 跳轉到另一個狀態。比如上面Primary處理NeedActingChange事件。

在事件處理中直接跳轉狀態是一種方式。
還有一種狀態跳轉方式,就是顯式的調用transit。比如:

boost::statechart::result PG::RecoveryState::Reset::react(const ActMap&)
{
    PG *pg = context< RecoveryMachine >().pg;
    ...

    ...
    pg->update_heartbeat_peers();
    pg->take_waiters();

    return transit< Started >();
}

事件處理

注意事件不一定都是在當前狀態下處理,如果當前狀態處理不了,可以在其父狀態處理,相當于子狀態繼承了父狀態的事件處理方法。

這里還有一個小疑問,我們看到有些地方執行post_event, 這些event是什么時候由誰執行的呢? 可以想象應該是狀態機在處理完當前事件之后就開始執行了。查看boost源碼也確實如此。
post_event的實現:

void post_event_impl( const event_base_ptr_type & pEvent )
{
   BOOST_ASSERT( get_pointer( pEvent ) != 0 );
   eventQueue_.push_back( pEvent );
}

eventQueue_是在process_event中循環處理的。

void process_event( const event_base_type & evt )
{
  if ( send_event( evt ) == detail::do_defer_event )
  {
    deferredEventQueue_.push_back( evt.intrusive_from_this() );
  }

  process_queued_events();
}

void process_queued_events()
{
  while ( !eventQueue_.empty() )
  {
    event_base_ptr_type pEvent = eventQueue_.front();
    eventQueue_.pop_front();

    if ( send_event( *pEvent ) == detail::do_defer_event )
    {
      deferredEventQueue_.push_back( pEvent );
    }
  }
}

有了上面的基礎之后,再去找各種狀態是怎么變化的就非常簡單了。

狀態機的事務

還有一點對于理解ceph狀態機也比較重要,我們經常看到狀態內部執行事務,但是沒看到事務在哪里提交。答案在這里:

if (!advance_pg(curmap->get_epoch(), pg, handle, &rctx, &split_pgs))
{
    // we need to requeue the PG explicitly since we didn't actually
    // handle an event
    peering_wq.queue(pg);
}
else
{
    assert(!pg->peering_queue.empty());
    PG::CephPeeringEvtRef evt = pg->peering_queue.front();
    pg->peering_queue.pop_front();
    pg->handle_peering_event(evt, &rctx);
}
need_up_thru = pg->need_up_thru || need_up_thru;
same_interval_since = MAX(pg->info.history.same_interval_since,
                          same_interval_since);
pg->write_if_dirty(*rctx.transaction);
if (!split_pgs.empty())
{
    rctx.on_applied->add(new C_CompleteSplits(this, split_pgs));
    split_pgs.clear();
}
dispatch_context_transaction(rctx, pg, &handle);

就在最后一行。

然后,好像沒了,自己對著狀態機那個圖研究吧 : )

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容