last_epoch_started介绍
Info中实际保存了两个last_epoch_started属 性,一个位于Info属性之下,另一个则位于Info的history属性之 下。
1. info.last_epoch_started由每个副本收到Primary发送的Peering完成通知之后更新,并与Peering结果一并存盘。
2. info.history.last_epoch_started则由Primary在收到所有副本 Peering完成通知应答之后更新和存盘。由于Primary更新完history 中的last_epoch_started属性之后会同步设置PG为Active状态(注意如果此时Acting中的副本数小于存储池最小副本数,PG将 进入Peered状态), 因此history中last_epoch_started也用于指示PG最近一次开始接受 客户端读写请求时的Epoch。
last_epoch_started字段有两个地方出现,一个是pg_info_t结构里的last_epoch_started,代表最后一次Peering成功后的epoch值,是本地PG完成Peering后就设置的。另一个是pg_history_t结构体里的last_epoch_started,是PG里所有的OSD都完成Peering后设置的epoch值。如下图所示:
info.last_epoch_started: 记录了PG在interval i
完成Peering时的epoch值,在i(包括i)之前的interval提交的所有写操作均会反映到local info/log中,而对于i之后的interval所提交的写操作将不会在当前local info/log中得到体现。由于提交的写操作不可能出现分歧(注:peering已经完成),因此即使用一个更旧的info.last_epoch_started来获取权威log/info信息,也不可能会影响到当前的info.last_epoch_started。
info.history.last_epoch_started: 记录了最近一个interval中,PG作为一个整体完成peering操作后设置的epoch值。由于提交的所有写操作都是由acting set中的OSD提交的,任何无歧义的write操作都会确保acting set中每一个OSD都记录了history.last_epoch_started。
如下我们介绍一下last_epoch_started的更新操作:
1) info.last_epoch_started: PG在本OSD上完成Peering操作,就会马上更新其对应的last_epoch_started的值。参看PG::activate()函数
2) info.history.last_epoch_started: 对于本字段的更新操作,情况较为复杂。
对于PG副本OSD上的info.history.last_epoch_started字段的更新会在Peering完成后马上进行,参看PG::activate();另外还会在Primary OSD向副本OSD发送pg_query_t::INFO时得到更新:
1 boost::statechart::result PG::RecoveryState::Stray::react(const MQuery& query) 2 { 3 PG *pg = context<RecoveryMachine>().pg; 4 pg->fulfill_query(query, context<RecoveryMachine>().get_recovery_ctx()); 5 return discard_event(); 6 } 7 8 void PG::fulfill_query(const MQuery& query, RecoveryCtx *rctx) 9 { 10 if (query.query.type == pg_query_t::INFO) 11 { 12 ... 13 update_history(query.query.history); 14 ... 15 } 16 } 17 18 void PG::update_history(const pg_history_t& new_history) 19 { 20 if (info.history.merge(new_history)) 21 { 22 ... 23 } 24 on_info_history_change(); //scrub机制 25 } 26 27 bool merge(const pg_history_t &other) 28 { 29 ... 30 if (last_epoch_started < other.last_epoch_started) { 31 last_epoch_started = other.last_epoch_started; 32 modified = true; 33 } 34 ... 35 return modified; 36 }
1 void PG::activate(ObjectStore::Transaction& t, 2 epoch_t activation_epoch, 3 list<Context*>& tfin, 4 map<int, map<spg_t,pg_query_t> >& query_map, 5 map<int, 6 vector< 7 pair<pg_notify_t, 8 pg_interval_map_t> > > *activator_map, 9 RecoveryCtx *ctx) 10 { 11 ... 12 13 // find out when we commit 14 t.register_on_complete( 15 new C_PG_ActivateCommitted( 16 this, 17 get_osdmap()->get_epoch(), 18 activation_epoch)); 19 20 ... 21 } 22 23 void PG::_activate_committed(epoch_t epoch, epoch_t activation_epoch) 24 { 25 if (pg_has_reset_since(epoch)) { 26 ... 27 } else if (is_primary()) { 28 ... 29 if (peer_activated.size() == actingbackfill.size()) 30 all_activated_and_committed(); 31 } 32 ... 33 } 34 35 void PG::all_activated_and_committed() 36 { 37 ... 38 queue_peering_event( 39 CephPeeringEvtRef( 40 std::make_shared<CephPeeringEvt>( 41 get_osdmap()->get_epoch(), 42 get_osdmap()->get_epoch(), 43 AllReplicasActivated()))); 44 } 45 46 boost::statechart::result PG::RecoveryState::Active::react(const AllReplicasActivated &evt){ 47 ... 48 49 // info.last_epoch_started is set during activate() 50 pg->info.history.last_epoch_started = pg->info.last_epoch_started; 51 52 ... 53 }