EOS節點爲什麼需要區塊回放,回放的過程是怎樣的,回放完畢後結果如何?
這些問題我們將在下面逐一講述。
區塊回放的前提條件
EOS服務節點爲什麼需要區塊回放呢?
EOSIO運行主網中,各個服務節點基於區塊保持了狀態一致性。當出現某個節點與其他節點的狀態不一致,無法持續保證一致性的情況下,這個時候就需要進行區塊回放。
那麼,服務節點什麼時候進行區塊回放呢?基本上是兩種情況,下面分別講述:
a.當服務節點運行出錯重啓服務提示數據庫髒數據時,這個時候節點的啓動命令中就會帶有回放的參數,節點需要根據自身的情況,清除髒數據,從正常主網節點中同步所有不可逆區塊到節點本地,並對這些區塊按照塊序號進行回放,如果本地節點還有可用的可逆塊表數據,則再將可逆塊按順序逐個回放。
如下圖所示,整個網絡上目前是節點B出錯或異常重啓,所以節點B就需要執行區塊同步,然後將創世區塊外的所有區塊進行回放來對全網節點的區塊基線進行追趕:
b.當需要新增服務節點時,這個時候節點啓動後加入生產者列表,會從正常主網節點中同步所有不可逆區塊到節點本地,並對這些區塊按照塊序號進行回放。
如下圖所示,整個網絡上目前存在節點A,B,現在新增節點C,節點C會先執行所有不可逆區塊同步,然後將創世區塊外的所有區塊進行回放來對全網節點的區塊基線進行追趕:
區塊回放調用的地方
eos節點調用進行replay()的地方只在節點初始化的時候,對應init()函數,如下圖所示:
區塊回放過程的解析
當一個區塊落後節點試圖追趕全網節點的區塊基線時,主要的步驟是分成三步來執行:
a. 先通過net_plugin網絡插件從任何一個正常節點進行區塊同步,將這些區塊獲取到本節點
b. 然後根據區塊的塊序號順序,逐個對區塊上的交易按照交易順序進行執行
c. 如果本地緩存了可逆塊表數據,則接續不可逆區塊,將所有可逆區塊上的交易按順序進行執行
第2,3步循環處理區塊交易的步驟就是區塊的回放。下圖爲三個步驟的示意:
我們再來看下區塊回放的代碼:
其中關鍵處理函數是replay_push_block(),這個是針對單個區塊進行回放的函數,其實現代碼如下:
其中的主要功能實現代碼是通過maybe_switch_forks()函數實現的,而該函數的處理思想則是:
處理區塊鏈分叉要儘量保證鏈的可用性和持續運行,因此,當嘗試最長鏈切換失敗時,就放棄該分支,繼續使用當前分支即可。該函數的具體的實現流程解析請見文章《節點接收區塊後的maybe_switch_forks的處理流程解析》。
區塊回放的結果
節點區塊回放完畢後,此時當前節點應該與全網其他節點的不可逆區塊達到一致,即落盤存儲的不可逆區塊數目和序號都完全一致。全網各個節點當前的唯一差異就在於個節點上的可逆區塊表內容及交易隊列內容不一致,如下圖所示。
此時,新增或重啓節點的狀態就正常了,可以參與正常的生產流程了。