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
};
下面簡單畫出三者之間的關系示意圖:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmL4ITM5EjN2QDOx0iN5MDOzMzM1ETMwITMxIDMy0SOzAzMwMTMvwlMxEjMwIzLclzMwMDMzEzLcd2bsJ2Lc12bj5ycn9Gbi52YuAjMwIzZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
其中:
- 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才會發生變化。
如下圖所示:
- 根據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。例如:
參考資料
1. ceph中PGLog處理流程