本节我们介绍一下PG Recovery过程中的一些重要数据结构。
1. RecoveryCtx数据结构
RecoveryCtx作为一次恢复操作的上下文,我们介绍一下其中几个比较重要的字段:
-
query_map: 用于缓存PG Query查询信息,后续会将这些缓存信息构造成MOSDPGQuery消息,然后发送到对应的OSD上。query_map的key部分为OSD的序号。
-
info_map: 用于缓存pg_notify_t信息,后续会将这些缓存信息构造成MOSDPGInfo查询的消息,然后发送到对应的OSD上。info_map的key部分为OSD的序号。
-
notify_list:用于缓存pg_notify_t信息,后续会将这些缓存信息构造成MOSDPGNotify消息,然后发送到对应的OSD上。notify_list的key部分为OSD的序号
-
transaction:本RecoveryCtx所关联的事务。在恢复过程中可能涉及到需要将相关信息持久化,就通过此transaction来完成
-
handle:ThreadPool::TPHandle的主要作用在于监视线程池中每一个线程的执行时常。每次线程函数执行时,都会设置一个grace超时时间,当线程执行超过该时间,就认为是unhealthy的状态。当执行时间超过suicide_grace时,OSD就会产生断言而导致自杀。这里向transaction对应的ObjectStore传入handle参数,主要是为了处理超时方面的问题
1.1 RecoveryCtx的使用
在OSD中,通常使用如下函数来创建RecoveryCtx对象:
之后,调用如下函数将对应map里面的数据发送出去:
2. NamedState数据结构
NamedState主要是用于对一种状态进行命名。
3. statechart状态机
Ceph在处理PG的状态转换时,使用了boost库提供的statechart状态机。因此,这里先简单介绍一下statechart状态机的基本概念和涉及的相关知识,以便更好地理解Peering过程PG的状态转换流程。
3.1 状态
在statechart里,状态的定义有两种方式:
定义一个状态需要继承boost::statechart::simple_state或者boost::statechart::state类。上面Reset状态继承了boost::statechart::state类。该类的模板参数中,第一个参数为状态机自己的名字Reset,第二个参数为所属状态机的名字,表明Reset是状态机RecoveryMachine的一个状态。
状态Started
也是状态机RecoveryMachine的一个状态,模板参数中多了一个参数Start
,它是状态Started
的默认初始子状态,其定义如下:
上面定义的状态Start
是状态Started的子状态。第一个模板参数是自己的名字,第二个模板参数是该子状态所属父状态的名字。
综上所述,一个状态,要么属于一个状态机,要么属于一个状态,成为该状态的子状态。其定义的模板参数是自己,第二个模板参数是拥有者,第三个模板参数是它的起始子状态。
3.2 事件
状态能够接收并处理事件。事件可以改变状态,促使状态发生转移。在boost库的statechart状态机中定义事件的方式如下所示:
QueryState为一个事件,需要继承boost::statechart::event类,模板参数为自己的名字。
3.3 状态响应事件
在一个状态内部,需要定义状态机处于当前状态时,可以接受的事件以及如何处理这些事件的方法:
3.3.1 可处理的事件列表及处理对应事件的方法
上述代码列出了状态RecoveryMachine/Initial可以处理的事件列表和处理对应事件的方法:
1) 通过boost::mpl::list定义该状态可以处理多个事件类型。在本例中可以处理Initialize
、Load
、NullEvt
和event_base
事件。
2)简单事件处理
定义了状态Initial接收到事件Initialize后,无条件直接跳转到Reset状态;
3) 用户自定义事件处理: 当接收到事件后,需要根据一些条件来决定状态如何转移,这个逻辑需要用户自己定义实现
custom_reaction定义了一个用户自定义的事件处理方法,必须有一个react()的处理函数处理对应该事件。状态转移的逻辑需要用户自己在react函数里实现:
4) NullEvt事件用户自定义处理,但是没有实现react()函数来处理,最终事件匹配了boost::statechart::event_base事件,直接调用函数discard_event把事件丢弃掉。
3.3.2 存在的一些疑问
1) 对于NullEvt事件
但是我们却并没有定义该事件的reaction函数。因为NullEvt继承自boost::statechart::event_base,因此其会调用如下函数来进行处理:
关于这一点,我们可以通过后面的statechart示例
来得到验证;
2) 对于MNotifyRec
、MInfoRec
以及MLogRec
事件
对于上面的3个事件,我们发现并没有将其添加到boost::mpl::list中,对于此类事件,会通过如下语句来进行处理:
关于这一点,我们也可以通过后面的statechart示例
来得到验证;
3) 对于boost::statechart::event_base事件,似乎有两种不同的处理方式
这里我们通过试验发现:
- 对于没有添加到boost::mpl::list中的事件,其默认就会调用如下来进行处理
-
对于添加到了boost::mpl::list中的事件,如果直接找不到对应的react函数,且没有定义其父类型boost::statechart::event_base的react函数,那么直接会编译时报错
-
对于添加到了boost::mpl::list中的事件,如果直接找不到对应的react函数的话,但是定义了其父类型boost::statechart::event_base的react函数,那么会调用其父类型的react函数来进行处理
3.4 状态机的定义
RecoveryMachine为定义的状态机,需要继承boost::statechart::state_machine类:
模板参数第一个参数为自己的名字,第二个参数为状态机默认的初始状态Initial。
状态机的基本操作有两个:
initiate()
是继承自boost::statechart::state_machine的成员函数。
- 函数process_event()用来向状态机投递事件,从而触发状态机接收并处理该事件
process_event()
也是继承自boost::statechart::state_machine的成员函数。
3.5 context函数
context是状态机的一个比较有用的函数,它可以获取当前状态的所有祖先状态的指针。通过它可以获取父状态以及祖先状态的一些内部参数和状态值。context()函数是实现在boost::statechart::state_machine中的:
同时context()函数在boost::statechart::simple_state中也有实现:
从simple_state的实现来看,可以获取当前状态的祖先状态指针,也可以获取当前状态所属状态机的指针。
例如状态Started
是RecoveryMachine的一个状态,状态Start是Started状态的一个子状态,那么如果当前状态是Start
,就可以通过该函数获取它的父状态Started
的指针:
同时也可以获取其祖先状态RecoveryMachine的指针:
综上所述,context()函数为获取当前状态的祖先状态上下文提供了一种方法。
3.6 事件的特殊处理
事件除了在状态转移列表中触发状态转移,或者进入用户自定义的状态处理函数,还可以有下列特殊的处理方式:
- 在用户自定义的函数里,可以直接调用
transit
来直接跳转到目标状态。例如:
可以直接跳转到状态Stray
。
- 在用户自定义的函数里,可以调用函数post_event()直接产生相应的事件,并投递给状态机
- 在用户的自定义函数里,调用函数discard_event()可以直接丢弃事件,不做任何处理
- 在用户的自定义函数里,调用函数forward_event()可以把当前事件继续投递给父状态机进行处理
3.7 statechart示例
上面的示例与PG中对于Initial状态的处理类似,下面我们来看一下分别执行上述语句时的打印情况:
Hello, Initial!
Initial event_base processed!
Hello, Initial!
Bye, Initial!
Hello, Crashed!
Hello, Initial!
Bye, Initial!
Hello, Crashed!
4. PG状态机
4.1 PG状态机中的所有事件
下面我们列出Recovery过程中的所有事件:
-
QueryState
-
MInfoRec
-
MLogRec
-
MNotifyRec
-
MQuery
-
AdvMap
-
ActMap
-
Activate
-
RequestBackfillPrio
-
TrivialEvent事件
-
MakePrimary
-
MakeStray
-
NeedActingChange
-
IsIncomplete
-
GotLog
-
SnapTrim
-
Reset
-
SnapTrimReserved
-
SnapTrimTimerReady
4.2 PG状态机中的所有状态及其对应的事件
- WaitRemoteBackfillReserved状态
- WaitLocalBackfillReserved状态
- RepWaitBackfillReserved状态
- RepWaitRecoveryReserved状态
- WaitRemoteRecoveryReserved状态
- WaitLocalRecoveryReserved状态
4.3 PG状态机
在类PG的内部定义了类RecoveryState,该类RecoveryState的内部定义了PG的状态机RecoveryMachine和它的各种状态。
在每个PG对象创建时,在构造函数里创建一个新的RecoveryState类的对象,并创建相应的RecoveryMachine类的对象,也就是创建了一个新的状态机。每个PG类对应一个独立的状态机来控制该PG的状态转换。
上面machine.initiate()调用的是boost::statechart::state_machine中的initiate()方法。
[参看]
-
ceph osd heartbeat 分析
-
boost官网
-
在线编译器
-
boost在线编译器