天天看點

【osd】ceph中PGLog處理流程

struct pg_log_entry_t {
    ObjectModDesc mod_desc;                 //用于儲存本地復原的一些資訊,用于EC模式下的復原操作

    bufferlist snaps;                       //克隆操作,用于記錄目前對象的snap清單
    hobject_t  soid;                        //操作的對象
    osd_reqid_t reqid;                      //請求唯一辨別(caller + tid)


    vector<pair<osd_reqid_t, version_t> > extra_reqids;


    eversion_t version;                     //本次操作的版本
    eversion_t prior_version;                //前一個操作的版本
    eversion_t reverting_to;                 //本次操作回退的版本(僅用于復原操作)

    version_t user_version;                 //使用者的版本号
    utime_t     mtime;                      //使用者的本地時間
    
    __s32      op;                          //操作的類型
    bool invalid_hash;                      // only when decoding sobject_t based entries
    bool invalid_pool;                      // only when decoding pool-less hobject based entries

    ...
};      
/**
 * pg_info_t - summary of PG statistics.
 *
 * some notes: 
 *  - last_complete implies we have all objects that existed as of that
 *    stamp, OR a newer object, OR have already applied a later delete.
 *  - if last_complete >= log.bottom, then we know pg contents thru log.head.
 *    otherwise, we have no idea what the pg is supposed to contain.
 */
struct pg_info_t {
    spg_t pgid;                    //對應的PG ID

    //PG内最近一次更新的對象的版本,還沒有在所有OSD上完成更新。在last_update和last_complete之間的操作表示
    //該操作已在部分OSD上完成,但是還沒有全部完成。
    eversion_t last_update;        
    eversion_t last_complete;      //該指針之前的版本都已經在所有的OSD上完成更新(隻表示記憶體更新完成)

    epoch_t last_epoch_started;    //本PG在啟動時候的epoch值
    
    version_t last_user_version;   //最後更新的user object的版本号
    
    eversion_t log_tail;           //用于記錄日志的尾部版本
    
    //上一次backfill操作的對象指針。如果該OSD的Backfill操作沒有完成,那麼[last_bakfill, last_complete)之間的對象可能
    //處于missing狀态
    hobject_t last_backfill;      


    bool last_backfill_bitwise;            //true if last_backfill reflects a bitwise (vs nibblewise) sort
    
    interval_set<snapid_t> purged_snaps;   //PG要删除的snap集合
    
    pg_stat_t stats;                       //PG的統計資訊
    
    pg_history_t history;                  //用于儲存最近一次PG peering擷取到的epoch等相關資訊
    pg_hit_set_history_t hit_set;          //這是Cache Tier用的hit_set
};      

下面簡單畫出三者之間的關系示意圖:

【osd】ceph中PGLog處理流程

其中:

  • last_complete: 在該指針

    之前

    的版本都已經在所有的OSD上完成更新(隻表示記憶體更新完成);
  • last_update: PG内最近一次更新的對象的版本,還沒有在所有OSD上完成更新。在last_update與last_complete之間的操作表示該操作已在部分OSD上完成,但是還沒有全部完成。
  • log_tail: 指向pg log最老的那條記錄;
  • head: 最新的pg log記錄
  • tail: 指向最老的pg log記錄的前一個;
  • log: 存放實際的pglog記錄的list

從上面結構可以得知,PGLog裡隻有對象更新操作相關的内容,沒有具體的資料以及偏移大小等,是以後續以PGLog來進行恢複時都是按照整個對象來進行恢複的(預設對象大小是4MB)。

另外,這裡再介紹兩個概念:

  • epoch是一個單調遞增序列,其序列由monitor負責維護,當叢集中的配置及OSD狀态(up、down、in、out)發生變更時,其數值加1。這一機制等同于時間軸,每次序列變化是時間軸上的點。這裡說的epoch是針對OSD的,具體到PG時,即對于每個PG的版本eversion中的epoch的變化并不是跟随叢集epoch變化的,而是目前PG所在OSD的狀态變化,目前PG的epoch才會發生變化。

如下圖所示:

【osd】ceph中PGLog處理流程
  • 根據epoch增長的概念,即引入第二個重要概念interval

因為pg的epoch在其變化的時間軸上并非是完全連續的,是以在每兩個變化的pg epoch所經曆的時間段我們稱之為intervals。

3.1.3 Trim Log

void PrimaryLogPG::execute_ctx(OpContext *ctx)
{
    ......
      // trim log?
  if (hard_limit_pglog())
    calc_trim_to_aggressive();
  else
    calc_trim_to();
    ......
}      

前面說到PGLog的記錄數是有限制的,正常情況下預設是3000條(由參數osd_min_pg_log_entries控制),PG降級情況下預設增加到10000條(由參數osd_max_pg_log_entries)。當達到限制時,就會trim log進行截斷。

在ReplicatedPG::execute_ctx()裡調用ReplicatedPG::calc_trim_to()來進行計算。計算的時候從log的tail(tail指向最老的記錄的前一個)開始,需要trim的條數為

log.head - log.tail - max_entries

。但是trim的時候需要考慮到min_last_complete_ondisk(這個表示各個副本上last_complete的最小版本,是主OSD在收到3個副本都完成時再進行計算的,也就是計算last_complete_ondisk和其他副本OSD上的last_complete_ondisk,即peer_last_complete_ondisk的最小值得到min_last_complete_ondisk),也就是說trim的時候不能超過min_last_complete_ondisk,因為超過了也trim掉的話就會導緻沒有更新到磁盤上的pg log丢失。是以說可能存在某個時刻,pglog的記錄數超過max_entries。例如:

【osd】ceph中PGLog處理流程

參考資料

1. ceph中PGLog處理流程