嗨,大家好!歡迎來到C-Shopping,這是一場揭開科技面紗的電商之旅。我是C-Shopping開源作者“繼小鵬”,今天將為你介紹一款基於最新技術的開源電商平臺。讓我們一同探索吧! 點擊 這裡,http://shop.huanghanlian.com/,即刻踏上C-Shopping 體驗之旅! 項 ...
原文鏈接:https://cwiki.apache.org/confluence/display/KAFKA/KIP-405%3A+Kafka+Tiered+Storage
譯者:Kafka KIP-405是一篇非常優秀的多層存儲的設計稿,不過此設計稿涉及內容很多,文章量大、嚴謹、知識點諸多。我們國內還沒有對其有相對完整的譯文,面對如此上乘的文章,譯者想降低其門檻,讓國內更多的人瞭解其設計,因此花費了諸多時間精力將此文進行了全文翻譯,同時有一些可能讓人產生疑惑的技術細節,譯者也都打上了註釋,希望可以幫助更多的人。當然如果有一些Kafka基礎,且英文閱讀流暢的話,譯者還是建議去看原文
背景
Kafka是基礎數據重要的組成部分,並且已經得到用戶廣泛的認可,增長勢頭迅猛。隨著集群規模的增加,越來越多的數據將會被存儲在Kafka上,其消息的保留時長、集群的彈縮、性能以及運維等日益變得越來越重要
Kafka採用append-only的日誌追加模式,將數據存在在本地磁碟中。消息保留時長通過配置項log.retention
來進行控制,既可以設置全局層面的,同時也可以設計某個topic維度的。消息保留時長能否確保數據持久化不丟失,即便是consumer短暫性宕機或不可用,當其成功重啟後,只要時間沒有超過log.retention
,消息依舊能夠讀取
總的消息的存儲量,與topic/partition數量、消息存儲速率、消息保留時長相關,一個Kafka的Broker通常在本地磁碟上存儲了大量的數據,例如10TB,這種大量本地存儲的現象給Kafka的維護帶來了巨大挑戰
Kafka作為一種長期的存儲服務
Kafka的普及率越來越高,也逐漸成為了很多數據的入口。它會將數據持久化下來,因此允許用戶進行一些非實時的消費操作。很多用戶因為Kafka協議的簡單以及消費者API的廣泛採用,且允許用戶將數據保留很長一段時間,這些特性都有助於Kafka日益成為了數據的source of data(SOT)
目前,Kafka一般會配置一個較短的保留時長(例如3天),然後更老的數據可以通過數據管道拷貝至更具彈縮能力的外部存儲(例如HDFS)以便長期使用,結果就是客戶端需要建立2種機制去讀取數據,相對新的數據讀取Kafka,老數據則讀取HDFS
Kafka存儲的提高,一般是依賴增加更多的Broker節點來實現的,但是這樣同樣也會導致新增了更多的記憶體+cpu,相對比可彈縮的外部存儲來講,這樣無疑是增加了全局的開銷,並且一個很多節點的集群同樣增加了運維、部署的難度
Kafka本地存儲以及維護的複雜性
當Kafka的一個broker壞掉了,將會用一個新的broker來替代,然後這個新節點必須從其他節點上拉取舊節點的全量數據。同樣,當新添加一個broker來橫向擴展集群存儲時,集群的rebalance會為新節點分配分區,這同樣需要複製大量的數據。恢復及rebalance的耗時與kafka broker上的數據量呈正相關。許多多broker的集群(例如100個broker),節點故障是非常常見的情況,在恢復過程中消耗了大量的時間,這使得運維操作變得非常困難
減少每個broker上的存儲數據量能夠減少recovery及rebalance時間,但是這樣操作的話同樣需要減少消息的保留時長,這樣就使得Kafka可提供的消息回溯時間變得更少
Kafka上雲
本地部署的Kafka一般都會使用多個具備硬體SKU的高容量磁碟,從而最大程度提高I/O的吞吐量。而在雲上,具有類似SKU的本地磁碟,要麼不可用,要麼非常昂貴。如果Kafka能夠使用容量較小的SKU作為本地存儲,那麼它就更適合上雲
解決方案 - Kafka分層存儲
Kafka數據主要以流式方式使用尾部讀取來進行消費,提供讀取的層,一般都是操作系統的Page Cache,而不是穿透到磁碟。而舊的數據一般是為了回溯或者是因為consumer故障後重啟後讀取的,而這種情況一般不太常見。
在分層存儲方法中,Kafka集群配置有兩層存儲:本地和遠程(local and remote)。本地存儲層與當前的Kafka相同,使用Kafka Broker上的本地磁碟來存儲日誌段。而新的遠端存儲層則使用一些外部存儲,例如HDFS或者S3來實現。不同的存儲層使用不同的日誌過期時間。當開啟遠程存儲時,本地消息的保留時長將會從幾天縮短至幾小時,而遠端存儲的消息保留時長則可能會保留更長的時間,例如幾周甚至幾個月。當本地日誌段發生了滾動 (譯者:這裡所謂的滾動rolled,可以簡單理解為某個日誌段寫滿1G了,即數據已經不會再發生變化了),它可能就會被拷貝至遠端存儲,當然包含日誌段相關的索引文件。這樣即便是延遲敏感的數據也能獲得高效的消費,因為數據都是尾部讀取,且數據都會高概率命中page cache。而那些讀取歷史消息,或者對消息進行回溯的場景,很有可能數據已經不在本地存儲了,那麼它們將會去遠端存儲上讀取
此解決方案允許在Kafka集群擴容存儲時,將不再依賴於記憶體和CPU,使Kafka成為一個長期存儲的解決方案。同時也減少了每個broker上本地存儲的數據量,從而減少了集群recovery及rebalance時需要複製的數據量。broker不需要恢復遠程存儲層中的日誌段,也不存在惰性恢復,而是遠程存儲層直接提供服務。這樣,增加消息保留時長就不需要再擴展Kafka集群的broker數量了,同時消息總體的保留時長還可以更長,不用像當前很多集群部署的策略,需要啟動一個單獨的管道,將數據從Kafka拷貝至外部存儲了
Goals
通過將舊數據存儲在外部存儲(如HDFS或S3)中,實現了將Kafka的存儲擴展到了集群之外,不過Kafka的內部的協議不能有太大的變動。對於那麼沒有啟用分層存儲功能的現有用戶,Kafka各類行為及操作複雜性決不能改變
Non-Goals
- 分層存儲不能取代ETL管道任務。現有的ETL管道繼續按原樣消費Kafka的數據,儘管Kafka有更長的消息保留時長
- 二級存儲不適用於compact類型的topic。即便是將compact類型的topic的配置項
remote.storage.enable
設置為true,也不能將其類型由delete改為compact - 二級存儲不支持JBOD特性
變更
高層設計
RemoteLogManager
(RLM) 是一個新引入的組件:
- 處理leader變更、topic partition刪除等回調事件
- 可插拔的存儲管理器(即
RemoteStorageManager
)將處理segments的copy、read、delete事件,且其需要維護遠端segments日誌段的元數據(它需要知道哪些segments存儲在了遠端)
RemoteLogManager
是一個內部組件,不會向外暴露API
RemoteStorageManager
本身是一個介面,它定義了遠端日誌段及索引的生命周期。具體細節下文還會說明,我們將提供一個簡單的RSM的實現來幫助大家更好的理解它。而諸如HDFS或者S3的實現應該放在他們產品的倉庫中,Apache Kafka自身的倉庫不會包含其具體的實現。這個設計與Kafka connnector保持一致
譯者:其實這裡本質上Kafka定義了一套多層存儲的規範。突然想起一句話:普通的軟體在編碼,上流的軟體在設計,頂級的軟體在定義規範
RemoteLogMetadataManager
本身也是個介面,它同樣定義了具有強一致語義的遠端元數據的生命周期。它的預設實現是一個kafka系統內部的topic,用戶如果需要使用其他遠程存儲介質來存儲元數據的話,需要自己去擴展它
RemoteLogManager (RLM)
RLM為leader及follower啟動了很多任務,具體解析可見下文
- RLM Leader 職責
- 它會不斷地檢查非active狀態的LogSegments(這些LogSegments中最大的offset需要嚴格小於LSO,才能進行拷貝),然後將這些LogSegments及索引文件(offset/time/transaction/producer-snapshot)、leader epoch均拷貝至遠端存儲層
- 提供從遠端存儲層查詢舊數據的服務(當查詢的數據在local log存儲中沒有時)
- 即便是local存儲已經不足(或存儲的日誌已經超時 ?這裡存疑),也要先將日誌段LogSegments拷貝至遠端後,再刪除
- RLM Follower 職責
- 通過訪問RemoteLogMetdataManager來獲取遠端存儲的log及index數據
- 同時,它也會提供從遠端存儲層查詢舊數據的服務
RLM提供了一個本地的有界緩存(可能是LRU淘汰策略)來存儲遠端的索引文件,這樣可避免頻繁的訪問遠端存儲。它們存儲在log dir
目錄下的remote-log-index-cache
子目錄,這些索引可以像local索引一樣使用,用戶可以通過設置配置項remote.log.index.file.cache.total.size.mb
來設定此緩存的上限
在早期的設計中,還包含了通過遠端存儲的API拉取LogSegments元數據的章節,(譯者:這應該是曾經討論的某次中間版本)它在HDFS接入時,看起來一切運行的很好。依賴遠端存儲來維護元數據的問題之一是:整個分層存儲是需要強一致性的,它不僅影響元數據,還影響Segments日誌段數據本身。其次也要考慮遠端存儲中存儲元數據的耗時,在S3中,frequent LIST APIs
導致了巨大的開銷
譯者:主要是講為什麼要將元數據與日誌數據分開存儲的原因。這段可能讀起來有點摸不著頭腦,原因是咱們沒有參與他們之前的討論,之前的某個討論版本是想將日誌的元數據信息放入遠程存儲的,此處不用糾結
因此需要將遠端的數據本身,與元數據進行分離,其對應的管理類分別為RemoteStorageManager
、RemoteLogMetadataManager
本地及遠端offset約束
以下是leader offset相關描述圖
Lx = Local log start offset Lz = Local log end offset Ly = Last stable offset(LSO)
Ry = Remote log end offset Rx = Remote log start offset
Lz >= Ly >= Lx and Ly >= Ry >= Rx
譯者:這裡不做贅述,關鍵一點是remote offset中的最大值,是需要 <= LSO的
Replica Manager
譯者:註意,ReplicaManager是獨立存在的,在沒有引入多層存儲的時候,它就在,不過以前只管理local存儲罷了。它其實是RLM的上一層
如果配置了RLM,那麼ReplicaManager將調用RLM來分配或刪除topic-partition
如果某個Broker從Leader切換為了Follower,而正在此時,RLM正在工作,它正在將某個Segment拷貝至遠端,我們這個時候不會直接將其放棄掉,而是會等它完成工作。這個操作可能會導致Segment片段的重覆,但是沒關係,在遠端存儲的這些日誌過期後,均會刪除
譯者:為什麼會導致Segment片段的重覆呢? 因為很有可能新的leader已經對同一份Segment進行了上傳
Follower Replication
Overview
目前,followers從leaders拉取消息數據,並且儘力嘗試追上leader的log-end-offset(LEO),從而將自己的狀態變為in-sync副本。如果需要,follower可能還會截斷自己的日誌從而與leader的數據保持一致
譯者:Kafka為了保證數據的高可用,make leader的過程可能會對HW以上的記錄進行截斷
而在多級存儲中,follower同樣需要與leader的數據保持一致,follower僅複製leader中已經可用的本地存儲的消息。但是他們需要為遠端的Segment構建諸如「leader epoch cache」、「producer id snapshot」這些狀態,甚至有必要,它們還需要對其進行截斷
下麵這張圖對leader、follower、remote log、metadata storage 4者的關係進行了簡明的概述,具體的細節將在下文展開
- Leader將Segment日誌端及AuxiliaryState(含leader epoch及producer-id snapshots)拷貝至遠端存儲
- Leader將剛纔上傳的Segment日誌段的元數據發佈出去
- Follower從Leader拉取消息,並遵循一定的規範,這個規範在下文具體說明
- Follower等待Leader將元數據放入RemoteLogSegmentMetadataTopic後將其拉取下來
- Follower抓取相應的遠端存儲的元數據,並構建狀態AuxiliaryState
譯者:關於第2步,leader將元數據發佈出去,這裡需要註意的是,存儲partition元數據的介質並不一定是遠端存儲,預設實現是,kafka將其放在了一個內置的topic中,如上文提到的,如果用戶願意,可以將其擴展為一個遠程存儲
而這裡的partition元數據具體是指什麼呢?原文並沒有說明,其實就是每個Segment是存儲在了本地還是遠端,可根據這個元數據進行路由
Follower拉取消息協議細節
Leader epoch概念的引入,是為瞭解決在KIP-101及KIP-279中提到的leader切換的場景中,可能存儲日誌差異的問題。它(Leader epoch)是partition下的一個單調遞增的整數,每當leader進行了切換,那麼這個值將會累加,並且它也會存儲在消息的message batch中
Leader epoch文件存在於每個broker的每個partition中,然後所有狀態是in-sync的副本需要保證其有同樣的leader epoch歷史信息,以及相同的日誌數據
Leader epoch的作用:
- 決定日誌截斷(KIP-101)
- 保證副本間的一致性(KIP-279)
- 在發生截斷後,重置消費位點(KIP-320)
在使用遠端存儲時,我們應該像使用本地存儲一樣,來處理日誌及leader epoch
目前,純本地存儲的場景,follower從leader拉取消息後,通過讀取message batch來構建AuxiliaryState狀態。
譯者:這裡需要註意,純本地存儲的case是,follower需要不斷的從leader拉取消息,而這些消息會攜帶leader epoch 信息,從而維護自己的
leader-epoch-checkpoint
文件,kafka本身不提供專門的API來同步此文件信息,譯者認為這樣做也是比較合理的
而在多級存儲中,follower需要讀取leader構建出來的AuxiliaryState,從而獲取起始offset及leader epoch。然後follower將會從這個起始offset開始拉取數據。這個起始offset可能是「local-log-start-offset」或「last-tiered-offset」。local-log-start-offset是本地存儲的開始offset;last-tiered-offset是已經拷貝至遠端存儲的最大offset。我們來討論下使用這兩者的利弊
last-tiered-offset
- 用這個策略明顯的好處就是follower能否非常快的追上leader,因為follower只需要同步那些存在於leader本地存儲中,且還沒來得及放在遠端的日誌段
- 而這樣做的一個缺點是,follower相對於leader缺少很多本地日誌段,當這個follower成為leader後,其他follower將會根據新leader的log-start-offset來截斷它們的日誌段
譯者:關於這個缺點,是kafka自身的副本同步協議中定義的,因為follower不斷地從leader拉取消息,努力跟leader保持一致,一致不僅包括offset的上端,同時也包括offset的下端
local-log-start-offset
- 在發生leader切換時,將會保留本地日誌
- follower追趕leader,這將會花費較長的時間,當為某個partition新增一個全新follower時,就命中了這個case
基於上述原因,我們更傾向使用「local-log-start-offset」
在多層存儲中,當follower來拉取數據時,leader只會返回在本地存儲中存在的數據。那些已經存在在遠端,且本地已經沒有的日誌段,follower是不會進行拉取複製的。根據「local-log-start-offset」機制,如果有必要的話,follower可能會截斷自己的日誌
譯者:同上文,follower是會根據leader的local-log-start-offset來截斷自己日誌段的
當一個follower從leader拉取一個leader的本地存儲已經不存在的offset時,leader將會發送一個錯誤碼OFFSET_MOVED_TO_TIERED_STORAGE
,然後follower將會重新從leader獲取「local-log-start-offset」及「leader eopch」。follower收到leader的local-log-start-offset後,需要基於這個offset構建遠端日誌段的AuxiliaryState,「譯者:此處註意,在純local存儲的模式下,follower是通過拉取leader的全量日誌,並且在這個拉取過程中,逐步構建並維護leader-epoch-checkpoint文件的。而在多層存儲的環境中,因為follower不再需要從leader處拉取全量日誌,但是follower自身的leader-epoch-checkpoint文件還需要全量維護,因此就需要額外花精力去構建這個文件,否則當這個follower成為leader後,leader-epoch-checkpoint文件的部分缺失,會使其無法做出正常的判斷」這個AuxiliaryState其實就是leader的「leader eopch」及「producer-snapshot-ids」。可以通過兩種方式來實現:
- 引入一個新的協議,專門從leader中拉取這個AuxiliaryState
- 從遠端存儲中獲取這個AuxiliaryState
這裡更推薦後者,因為本身遠端存儲已經保留了這個欄位,且不需要在於leader的交互中引入新的協議
獲取目標offset的之前的日誌段的AuxiliaryState狀態需要以下2個步驟:
- 需要拉取遠端日誌段的元數據
- 需要在相應日誌段中拉取諸如leader epoch的記錄
當將一個日誌段(segment)搬移至遠端存儲後,leader broker同時需要將「leader epoch sequence」以及「producer id snapshot」追加到segment所在的目錄下。這些數據將會幫助follower來構建自己的「leader epoch sequence」以及「producer id snapshot」
譯者:原文其實反覆在強調這個事兒
因此,我們需要為這個副本引入一個相對應的新狀態,可以將其定義為BuildingRemoteLogAuxState
。follower的拉取線程就如同切換Fetching或Truncating states狀態一樣,在每次執行時,都需要判斷一下,需要切換至哪個狀態
當一個follower嘗試拉取一個已經不在leader local 存儲的offset時,會收到leader返回的OffsetMovedToRemoteStorage
錯誤,如果follower收到了這個狀態,將會:
- 通過調用API ListOffset來獲取leader的Earliest Local Offset (ELO) 以及 leader epoch (ELO-LE) 譯者:註意,ListOffset這個API將會發生改變,其返回的出參中將會攜帶這些信息
- 截斷自己的本地日誌以及AuxiliaryState
- 從Fetching狀態切換至BuildingRemoteLogAux狀態
處於BuildingRemoteLogAux狀態時,follower可以在以下兩個方案中二選一:
- 方案1:
- 通過不斷反覆調用FetchEarliestOffsetFromLeader API,從而獲取ELO-LE至leader中最早的leader epoch,然後構建follower本地的leader epoch。當遠端存儲上有很多任leader切換時,這個方案可能並不會很高效。不過這個方案的好處是,獲取leader epoch的操作完全在kafka內部,當遠端存儲出現短暫不可用時,follower仍然可以追趕leader併進入ISR
- 方案2:
- RLMM(RemoteLogMetadataManager)等待遠端的元數據,直到等到某個segment包含了ELO-LE
- 抓取遠端存儲的leader epoch以及producer snapshot(使用遠端fetcher線程)譯者:多層存儲引入的工作線程
- 獲取遠端存儲的leader epoch數據後,截取 [LSO, ELO] 部分,然後構建follower自己的cache
在構建完follower自己的leader epoch後,follower狀態轉換為Fetching,然後繼續從leader的ELO開始拉取數據。我們更傾向使用方案2,即從遠端存儲來獲取所需數據
Follower fetch 場景(包含日誌截斷的場景)
讓我們討論一下follower在嘗試從leader複製並從遠程存儲構建AuxiliaryState狀態時可能遇到的幾種情況
名詞定義:
OMTS : OffsetMovedToTieredStorage 譯者:offset已經不在leader中,通常是一個錯誤
ELO : Earliest-Local-Offset 譯者:local存儲中最早的offset
LE-x : Leader Epoch x, 譯者:leader epoch,不贅述
HW : High Watermark 譯者:高水位,kafka發明的詞,不贅述
seg-a-b: a remote segment with first-offset = a and last-offset = b 譯者:遠端存儲的某個segment日誌段,它的offse的區間
LE-x, y : A leader epoch sequence entry indicates leader-epoch x starts from offset y 譯者:leader epoch的某個區間
場景1:全新follower
現在假設某個全新的broker剛被加入集群,然後將其指派為某個partition的follower replica,這個follower肯定是沒有任何本地存儲數據的。它將會從offset為0的位置開始從leader抓取數據,如果offset為0的位點在leader中不存在的話,follower將會收到錯誤OFFSET_MOVED_TO_TIERED_STORAGE,然後follower將會給leader發送ListOffset API,並且在入參中攜帶參數timestamp = EARLIEST_LOCAL_TIMESTAMP,接著會收到leader返回的ELO(Earliest-Local-Offset) 譯者:多層存儲需要修改ListOffset協議
follower需要等待這個offset(leader的ELO)的返回,然後構建AuxiliaryState狀態,然後才能從leader拉取數據 譯者:又強調了構建覆核狀態的必要
步驟1:
抓取遠端segment信息,然後構建leader epoch
Broker A (Leader) |
Broker B (Follower) |
Remote Storage |
RL metadata storage |
3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 6: msg 6 LE-2 7: msg 7 LE-3 (HW) leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
1. Fetch LE-1, 0 2. Receives OMTS 3. Receives ELO 3, LE-1 4. Fetch remote segment info and build local leader epoch sequence until ELO leader_epochs LE-0, 0 LE-1, 3 |
seg-0-2, uuid-1 log: 0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 epochs: LE-0, 0 seg 3-5, uuid-2 log: 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 epochs: LE-0, 0 LE-1, 3 LE-2, 5 |
seg-0-2, uuid-1 segment epochs LE-0, 0 seg-3-5, uuid-2 segment epochs LE-1, 3 LE-2, 5 |
步驟2:
繼續從leader拉取數據
Broker A (Leader) |
Broker B (Follower) |
Remote Storage |
RL metadata storage |
3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 6: msg 6 LE-2 7: msg 7 LE-3 (HW) leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
Fetch from ELO to HW 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 6: msg 6 LE-2 7: msg 7 LE-3 (HW) leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
seg-0-2, uuid-1 log: 0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 epochs: LE-0, 0 seg 3-5, uuid-2 log: 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 epochs: LE-0, 0 LE-1, 3 LE-2, 5 |
seg-0-2, uuid-1 segment epochs LE-0, 0 seg-3-5, uuid-2 segment epochs LE-1, 3 LE-2, 5 |
場景2:out-of-sync follower catching up
一個follower正在嘗試追趕leader,然後leader對應的日誌段segment已經轉移至了遠端存儲。我們以目標日誌段是否在本地存儲來分為2種情況來討論
- 本地segment存在,而且本地最新的offset要比leader的ELO大
- 這種場景,本地存儲已有,follower跟常規方式一樣進行拉取即可
- 本地segment不存在,或者最新的offset要比leader的ELO小
- 這種場景,本地的日誌段可能因為日誌過期已經刪除,或者是因為follower已經離線了很長一段時間。然後follower拉取數據時,將會收到OFFSET_MOVED_TO_TIERED_STORAGE錯誤,然後follower將不得不截斷自己所有的本地日誌,因為這些數據在leader已經標記為過期
步驟1:
out-of-sync follower (broker B) 本地的offset存儲到了3
Broker A (Leader) |
Broker B (Follower) |
Remote Storage |
RL metadata storage |
0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 6: msg 6 LE-2 7: msg 7 LE-3 8: msg 8 LE-3 9: msg 9 LE-3 (HW) leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 3: msg 3 LE-1 leader_epochs LE-0, 0 LE-1, 3 1. Because the latest leader epoch in the local storage (LE-1) does not equal the current leader epoch (LE-3). The follower starts from the Truncating state. 2. fetchLeaderEpochEndOffsets(LE-1) returns 5, which is larger than the latest local offset. With the existing truncation logic, the local log is not truncated and it moves to Fetching state. |
seg-0-2, uuid-1 log: 0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 epochs: LE-0, 0 seg 3-5, uuid-2 log: 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 epochs: LE-0, 0 LE-1, 3 LE-2, 5 |
seg-0-2, uuid-1 segment epochs LE-0, 0 seg-3-5, uuid-2 segment epochs LE-1, 3 LE-2, 5 |
步驟2:
leader的本地日誌段因為數據過期而已經刪除,然後follower開始嘗試追上leader
Broker A (Leader) |
Broker B (Follower) |
Remote Storage |
RL metadata storage |
9: msg 9 LE-3 10: msg 10 LE-3 11: msg 11 LE-3 (HW) [segments till offset 8 were deleted] leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 3: msg 3 LE-1 leader_epochs LE-0, 0 LE-1, 3 <Fetch State> 1. Fetch from leader LE-1, 4 2. Receives OMTS, truncate local segments. 3. Fetch ELO, Receives ELO 9, LE-3 and moves to BuildingRemoteLogAux state |
seg-0-2, uuid-1 log: 0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 epochs: LE-0, 0 seg 3-5, uuid-2 log: 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 epochs: LE-0, 0 LE-1, 3 LE-2, 5 Seg 6-8, uuid-3, LE-3 log: 6: msg 6 LE-2 7: msg 7 LE-3 8: msg 8 LE-3 epochs: LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
seg-0-2, uuid-1 segment epochs LE-0, 0 seg-3-5, uuid-2 segment epochs LE-1, 3 LE-2, 5 seg-6-8, uuid-3 segment epochs LE-2, 5 LE-3, 7 |
步驟3:
刪除本地數據後,將會轉換為場景1一樣的case
Broker A (Leader) |
Broker B (Follower) |
Remote Storage |
RL metadata storage |
9: msg 9 LE-3 10: msg 10 LE-3 11: msg 11 LE-3 (HW) [segments till offset 8 were deleted] leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
1. follower rebuilds leader epoch sequence up to LE-3 using remote segment metadata and remote data leader_epochs LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 2. follower continue fetching from the leader from ELO (9, LE-3) 9: msg 9 LE-3 10: msg 10 LE-3 11: msg 11 LE-3 (HW) |
seg-0-2, uuid-1 log: 0: msg 0 LE-0 1: msg 1 LE-0 2: msg 2 LE-0 epochs: LE-0, 0 seg 3-5, uuid-2 log: 3: msg 3 LE-1 4: msg 4 LE-1 5: msg 5 LE-2 epochs: LE-0, 0 LE-1, 3 LE-2, 5 Seg 6-8, uuid-3, LE-3 log: 6: msg 6 LE-2 7: msg 7 LE-3 8: msg 8 LE-3 epochs: LE-0, 0 LE-1, 3 LE-2, 5 LE-3, 7 |
seg-0-2, uuid-1 segment epochs LE-0, 0 seg-3-5, uuid-2 segment epochs LE-1, 3 LE-2, 5 seg-6-8, uuid-3 segment epochs LE-2, 5 LE-3, 7 |
場景3:Multiple hard failures
步驟1:
Broker A已經將第一個segment轉移至了遠端存儲