1、 PG Peering的触发入口
需要执行peering的PG,最终需要入队:
|- OSD:: enqueue_peering_evt(
pgid,
PGPeeringEventRef(
std::make_shared<PGPeeringEvent>(
osdmap->get_epoch(),
osdmap->get_epoch(),
NullEvt())));
2、 PG Peering执行入口
|-void OSD::dequeue_peering_evt
|-advance_pg(curmap->get_epoch(), pg, handle, rctx) // 如果PG的MAP 大于等于 shard 的MAP,触发peering; 检查pg是否需要执行OSDMap更新
\-PG::do_peering_event
\-PeeringState::handle_event()
|-machine.process_event() // 向PG 状态机投递事件。NullEvt()事件等。触发PG peering的执行
3、 PG Peering状态机执行
1、状态机转换为start状态
|-Initial状态
\-boost::statechart::transition< Initialize, Reset >, // 收到Initialize事件,状态机转换为Reset状态
|-Started状态,自动转换为Start状态
\-PeeringState::Reset::react(const ActMap&) //收到ActMap事件
|-transit< Started >() // 转换为Started/start状态
2、状态机按照PG角色(主、从)触发执行
|-Start状态执行
|-PeeringState::Start::Start()
if (ps->is_primary()) {
post_event(MakePrimary()); //主进入Primary, Started, Peering 状态,执行PeeringState::Peering::Peering
} else { //is_stray
post_event(MakeStray()); // 从进入 Stray, Started 状态, 等待主更新
}
3、 主开始peering。
|-PeeringState::Peering::Peering(my_context ctx)
ps->state_set(PG_STATE_PEERING); // 设置PG外部状态为PG_STATE_PEERING,标识开始peering
4、 PG 主Peering,第一步:GetInfo
主开始peering,peering 第一步,执行GetInfo,获取PG副本的pg info信息(主要是pg的元数据,包含版本信息等)
4.1 向副本发送GetInfo 消息
|-PeeringState::GetInfo::GetInfo
ps->check_past_interval_bounds(); //检查info,.history中记录的Pg peering信息是否正确。及历史版本是否与info一致
|-PeeringState::log_weirdness() // 检查pg_log 与pg info版本的一致性
|-PastIntervals::PriorSet PeeringState::build_prior() // 获取PG所有版本的在当前OSDMap中为存活状态的osd,并判定PG是否为DOWN。
\-PastIntervals::PriorSet::PriorSet()
\- std::set<pg_shard_t> all_probe = past_intervals.get_all_probe(ec_pool); // 获取PG所有版本的osd
|- pi_compact_rep::get_all_participants
|- 判定PG 所有版本的osd (all_probe)是否处于 down状态,用osdmap 中记录的osd状态判定。
|- past_intervals.iterate_mayberw_back_to() // 检查PG版本大于l ast_epoch_started的interval(PG集合),其在现在的OSDMap中 ,对应的osd是否是down状态,如果在此间隔期PG不可修复,且有down 的,标识PG down、pg_down = true;
\- PeeringState::GetInfo::get_infos() // 对prior_set中probe成员(在当前osdmap中,PG所有版本为存活状态的osd),获取pg_info(pg的版本信息))
|-PeeringState::PeeringMachine::send_query
4.2 GetInfo 返回值(PG主收到副本发送的PG info 版本信息)
|-boost::statechart::result PeeringState::GetInfo::react(const MNotifyRec& infoevt)
\- PeeringState::proc_replica_info()
|-update_history(oinfo.history); // 用副本pg_info_t 中,history内容,更新本地pginfo的history。
|-if (!is_up(from) && !is_acting(from)) // 如果收到的副本成员,即不是acting成员,说明是PG老旧版本的成员。当PG当前数据已经修复完成,向该成员发送删除PG请求。
purge_strays(); // 向副本发送删除PG请求。
5、 PG 主Peering,第二步:GetLog
peering 第二步,执行GetLog,主要流程为:用第一步获取到的副本的pg info信息, 选举出一个权威副本(拥有最高版本的PG),及能够上线的副本(与权威副本日志有交集),获取权威副本的pg log信息。并将主的pg log与权威副本合并(对齐)。
5.1 选举权威副本,及上线副本,向权威副本发送GetLog 消息
|-PeeringState::GetLog::GetLog(my_context ctx)
\-PeeringState::choose_acting // 选出权威info , 并找出与权威info 有交集的副本acting_recovery_backfill(recovery修复),及与权威没有交集的up 副本backfill_targets,用于后续修复(backfill修复)。auth_log_shard为拥有权威info 的OSD
|-map<pg_shard_t, pg_info_t>::const_iterator auth_log_shard = find_best_info() //比较PG副本返回的PG info 信息, 选出一个:last_epoch_started最大,且last_update最大中的日志最长的log_tail
|-PeeringState::calc_replicated_acting() // 过滤出与权威info 有交集的成员。过滤顺序为:先过滤up成员,如果过滤出来总数量不足副本数,再过滤acting成员,如果过滤出来总数量不足副本数,再过滤其他获取到info的副本。
|-PeeringState::recoverable // 判定PG是否可修复, 即,与权威info 有交集的OSD数量,是否达到存储池要求的副本数量,
|-如果权威就是主本身,Getlog完成。
|-context<PeeringMachine>().send_query() // 主向权威发送pg_query_t::LOG消息, 获取权威的日志到本地。要求从request_log_from版本开始。
5.2 GetLog 返回值处理(PG主收到权威副本发送的PG Log信息)
|-boost::statechart::result PeeringState::GetLog::react(const GotLog&)
\-PeeringState::proc_master_log() // 将权威日志与本地日志进行合并(内存中),并记录日志能够识别的丢失对象。
\-PeeringState::merge_log
\-PGLog::merge_log() // 将本地日志与权威日志对齐。并解决分歧对象。(让对象处于正确的版本,miissing列表)
|-if (olog.tail < log.tail) // 如果权威日志尾部比主本地尾部长,将权威日志比主尾部长的部分的日志,增加到本地日志内存缓存中(以对象为维度的key等的索引日志,及日志内容)
|-log.index(*to);
|-log.log.splice(log.log.begin(), olog.log, from, to);
|-if (olog.head < log.head) // 如果本地日志头,比权威日志头长(分歧日志,解决分歧日志。
\-PGLog::rewind_divergent_log // 本地日志头比权威头长的部分为分歧日志
|-divergent = log.rewind_from_head(newhead) //拆剪出分歧日志
|-_merge_divergent_entries() // 将分歧日志中的对象,放到missing列表中。目的:分歧对象要回滚到对象的上一个版本。
|-if (olog.head > log.head // 权威日志头 长于本地日志头,
|-append_log_entries_update_missing() // 将权威长的部分追加到本地日志头,并加入missing列表,等待修复
|-peer_missing[from].claim(omissing); // 记录副本丢失对象列表
6、 PG 主Peering,第三步:GetMissing
GetMissing部分,主要是主获取上线副本的pg日志,将拉取到的上线副本的日志,与本地pg主日志进行合并(对齐),找出副本日志中的不一致对象,作为后续修复的依据。
6.1 GetMissing 发送拉取副本日志请求。
|-PeeringState::GetMissing::GetMissing // 对于与权威日志有交集的副本, 拉取副本日志,为修复做准备
|-context< PeeringMachine >().send_query() // pg_query_t::LOG, 拉取部分日志, pg_query_t::FULLLOG, 拉取全部日志
6.2 GetMissing 收到副本发送的日志。
|-boost::statechart::result PeeringState::GetMissing::react(const MLogRec& logevt)
\-PeeringState::proc_replica_log // 将返回的副本日志,与本地日志对齐(本地内存中,日志已经与权威日志一致), 并找到丢失对象版本放到missing列表中,(丢失对象,是指本地比权威日志长的那一部分——对象的丢失版本)
|-post_event(Activate(ps->get_osdmap_epoch())); // 如果所有副本都处理完成,进入Activate状态。
7 Active激活各个副本
7.1 主发消息激活副本
|-PeeringState::Active::Active(my_context ctx)
\-PeeringState::activate()
|-PG::schedule_event_on_commit() // 注册提交成功的回调函数,此回调函数 会将ActivateCommitted事件投递给PG状态机。
|-if (is_primary()) // 如果是主,向acting_recovery_backfill(与权威有交集)副本发送消息,发送主自己的I info ,log( 权威),MOSDPGLog类型的消息,激活该PG的从OSD上的副本
|-封装向所有即将上线的成员(acting_recovery_backfill)发送pg info,pg log。消息
|-if (pi.last_update == info.last_update) //pi为getinfo时取到的pginfo信息。 当PG最后更新的版本一致,说明该PG副本本身就是clean的,只发送pg_info来激活从OSD。M为消息封装。
|-m = new MOSDPGLog
|-else if( pg_log.get_tail() > pi.last_update || pi.last_backfill == hobject_t() ||
(backfill_targets.count(*i) && pi.last_backfill.is_max())) // 需要Backfill操作的OSD,发送pg_info,以及osd_min_pg_log_entries数量的PG日志
|-m = new MOSDPGLog
|-else // 需要Recovery操作的OSD,发送pg_info,以及从缺失的日志。
|-m = new MOSDPGLog
|-PG::send_cluster_message() // 向副本发送封装好的m消息。
|- 恢复地址的构建。
|-state_set(PG_STATE_ACTIVATING); // 设置PG状态
7.2 副本收到主发送的激活消息
副本收到主发送的pglog 或者 pginfo信息,会将自己本地的与主发送的对齐,并应答主MSG_OSD_PG_INFO消息。
1.收到MSG_OSD_PG_LOG
|-boost::statechart::result PeeringState::Stray::react(const MLogRec& logevt)
|-PeeringState::merge_log // 将本地log 与主发送的权威log对齐。
2 收到MSG_OSD_PG_INFO
2.2.7.3 主收到副本同步Pginfo,Pglog成功的MSG_OSD_PG_INFO消息
1.
|-boost::statechart::result PeeringState::Active::react(const MInfoRec& infoevt)
|-if (ps->peer_activated.size() == ps->acting_recovery_backfill.size()) // 如果主收到了所有副本同步pginfo pglog成功的应答消息。
|-PeeringState::Active::all_activated_and_committed()
|-post_event(PeeringState::AllReplicasActivated()); // 触发所有副本执行成功函数
2.
boost::statechart::result PeeringState::Active::react(const AllReplicasActivated &evt)