mnesia裏的lamport clock

mnesia使用"wait-die"機制預防死鎖,"wait-die"是基於時間戳的,mnesia採用了lamport clock算法做爲"wait-die"機制的時間戳。 java

Lamport clock是解決分佈式系統中事件發生時序的一種方式。 Lamport定義了一個關係叫作happens-before,記爲-> 。a->b意味着全部的進程都贊成事件a發生在事件b以前。在如下兩種狀況下,能夠很容易的獲得這個關係: node

1)若是事件a和事件b是同一個進程中的而且事件a發生在事件b前面,那麼a->b 算法

2)若是進程A發送一條消息m給進程B,a表明進程A發送消息m的事件,b表明進程B接收消息m的事件,那麼a->b 網絡

Lamport logical clock算法大體實現爲: app

每一個進程Pi維護一個本地計數器Ci,至關於logical clock,按照如下規則更新Ci: 分佈式

1)每次執行一個事件(例如經過網絡發送消息,或者將消息交換給應用層,或者其餘的一些內部事件 )以前,將Ci加1 oop

2)當進程Pi發送消息m給Pj的時候,在消息m上附上Ci code

3)當接收進程Pj接收到進程Pi發送的消息時,更新本身的Cj = max{Cj,Ci} 進程

Lamport論文連接:http://www.stanford.edu/class/cs240/readings/lamport.pdf 事件

===========================================================

在mnesia中充當這個計數器的就是事務ID,事務ID統一由mnesia_tm進程負責初始化和更新。mnesia_locker進程就是根據比較事務ID的大小來執行"wait-die"機制和防止餓死現象的。

allowed_to_be_queued(WaitForTid, Tid) ->
    case get(pid_sort_order) of
	undefined -> WaitForTid > Tid;
	r9b_plain ->
	    cmp_tid(true, WaitForTid, Tid) =:= 1;
	standard  ->
	    cmp_tid(false, WaitForTid, Tid) =:= 1
    end.
mnesia主要在如下幾種狀況下更新事務ID

1)本節點請求開始新事務時

在執行一個新事務前,會向mnesia_tm進程獲取一個事務ID和臨時存儲空間,mnesia_tm收到請求後會將事務ID計數器加1並賦給該事務。

mnesia_tm.erl:

doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=Sup}=State) ->
    receive
    ...
    {From, start_outer} ->
        case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of
        Etab ->
            tmlink(From),
            C = mnesia_recover:incr_trans_tid_serial(),
            ?ets_insert(Etab, {nodes, node()}),
            Tid = #tid{pid = tmpid(From), counter = C},
            ...

mnesia_recover.erl:

incr_trans_tid_serial() ->
    ?ets_update_counter(mnesia_decision, serial, 1).
2)參與其餘節點發起的事務結束時

事務涉及多個節點時,事務發起者會將帶有本節點事務ID的信息告訴全部參與的節點。當參與節點處理事務結束時,無論是正常結束仍是異常結束,都會更新本節點的事務計數器。

mnesia_tm.erl:

transaction_terminated(Tid)  ->
    mnesia_checkpoint:tm_exit_pending(Tid),
    Pid = Tid#tid.pid,
    if
	node(Pid) == node() ->
	    unlink(Pid);
	true ->  %% Do the Lamport thing here
	    mnesia_recover:sync_trans_tid_serial(Tid)
    end.

mnesia_recover.erl:

sync_trans_tid_serial(ThatCounter) when is_integer(ThatCounter) ->
    ThisCounter = trans_tid_serial(),
    if
	ThatCounter > ThisCounter ->
	    set_trans_tid_serial(ThatCounter + 1);
	true ->
	    ignore
    end;
sync_trans_tid_serial(Tid) ->
    sync_trans_tid_serial(Tid#tid.counter).

set_trans_tid_serial(Val) ->
    ?ets_insert(mnesia_decision, {trans_tid, serial, Val}).
其餘狀況:例如,mnesia啓動進行dump時、事務過程當中出現異常,向其餘節點詢問事務結果時也都會進行事務計數器的更新。 
相關文章
相關標籤/搜索