---
# System prepended metadata

title: '1-3 Time, Clocks, and the Ordering of Events in a Distributed System'
tags: [CAP Theorem]

---

# 1-3 Time, Clocks, and the Ordering of Events in a Distributed System
## The Partial Ordering
每個 process 裡面有許多事件(event)。看你怎麼實作，執行 subprogram 可以是事件、執行一條 machine instruction 可以是事件。
我們可以假設 process 裡面的 event 可以形成一個順序，可以說 $a$ occurs before $b$ 如果 $a$ happens before $b$。
我們可以把*這個定義*(happens before)延伸到多個 subprocess。
### happened before
:::info
happened before ($\to$) 定義：
    以下 $a, b$ 皆為事件(event)
(1) 如果 $a$ 跟 $b$ 事件在同一 process ，且 $a$ 比 $b$ 先執行，那麼 $a\to b$
    (2) 如果 $a$ 是訊息發送端， $b$ 是訊息接收端(兩者不同 process)，那麼 $a\to b$
    (3) 如果 $a\to b$ 且 $b\to c$ ，那麼 $a\to c$
:::
事件 $a, b$ 為 concurrent 的條件是 $a\not\to b$ 且 $b\not\to a$
我們假設 $a\not\to a$ ($a$ happened before $a$ 不合邏輯)。

==這代表 $\to$ 是*非反身性偏序*的(irreflexive partial ordering)==


![upload_efcad7836d81260ff8e6a3a273b814d4](https://hackmd.io/_uploads/HJh3C-gR6.png =60%x)


以圖1當中為例，$p_1 \to r_4$。($p_1\to q_2\to q_3\to q_4\to r_3\to r_4$)
:::info
$a\to b$ 也可以解釋成： $a$ 可以因果效應影響 $b$ (it is possible for event a to causally affect event b)
:::
==如果兩個事件之間不存在因果關係，那他們為 concurrent。例如$p_3, q_3$。==


## Logical Clocks
Clock 在此定義為一個單純為事件發給數字的東西。(例如今天早上0600起床，你為起床這個事件給了一個0600的數字)
我們定義每個 process $P_i$ 有一個 clock $C_i$，其能為每個 event $a$ 發給一個數字。
整個系統的 clock 定義為 $C$ ，其可為任何事件 $b$ 發給數字 $C(b)$，其 $C(b) = C_j(b)$ 如果 $b$ 屬於 $P_j$
這邊並不保證 $C_i$ 給的數字跟物理時鐘的數字有關係。所以可以把 $C_i$ 想成 logical clock。他們可用任意方式實作。

如果事件 $a$ occurs before $b$，那 $a$ 應該發生於比 $b$ 還早的時間。更正式地定義如下：
### Clock Condition
:::info
Clock Condition. For any events a, b:
if $a\to b$ then $C(a) < C(b)$
:::
:::danger
這邊我們並不能說他的逆命題(converse condition)為真
if $C(a) < C(b)$ then $a\to b$
因為那樣代表所有 concurrent 事件要同時發生。
[證明](https://cs.stackexchange.com/questions/131744/why-does-the-converse-of-the-clock-condition-imply-that-any-two-concurrent-event)
:::
:::info
Clock Condition 滿足條件：
**C1**: 如果 $a, b$ 同屬 $P_i$，且 a 比 b 早，那麼 $C_i(a) < C_i(b)$
**C2**: 如果 $P_i$ 的 $a$ 發訊息給 $P_j$ 的 $b$，那麼 $C_i(a) < C_j(b)$
:::

我們想像 process 的 clock 上的數字會跳動走訪每個數字，跳動發生的時間在事件跟事件之間。
例如 $P_i$ 中的 $a, b$，$C_i(a)=4, C_i(b)=7$，那麼 clock 就會於事件 $a, b$ 中間跳動走訪過 5, 6, 7。
我們把每個跳動發生畫在 Fig. 1 中的虛線，可得到 Fig. 3
![upload_024b513ea4ef999aa53f526660a9f586](https://hackmd.io/_uploads/Sk7sR-lA6.png =60%x)


:::info
**C1** 代表同 process 的兩個事件中一定有虛線。
**C2** 代表訊息的傳遞一定會經過虛線。
:::
更動 clock $C_i$ 的值並不算是一種事件(event)。
接下來討論如何實作滿足上述兩個條件的時鐘。

滿足 **C1** 只需要 **IR1**。
為了滿足 **C2** ，我們需要發送端有個時間戳記 $T_m$ ，然後接收端在收到的當下要把自己的時鐘設為比 $T_m$ 還晚。
:::info
**IR1**: 每個 process $P_i$ 應該在兩個連續的 event 中增加 $C_i$ 的值。
**IR2**: 
    (a) 如果 $a$ 是訊息 $m$ 的傳遞者，那麼訊息 $m$ 應該包括時間戳記 $T_m=C_i(a)$
    (b) $P_j$ 收到訊息的當下要把自己的 $C_j$ 的值設為 $max(C_j, T_m+1)$
:::


## Ordering the Events Totally
我們可以使用一系列符合 **Clock Condition** 的 clock 來為 events 做完全排序(total ordering)
我們只需要按照事件發生的時間來排序就好。
不過要破解同時發生，只需要另外定義任意關係 $\prec$ 來排序 processes。
我們定義 $\Rightarrow$ 如下：
### 定義 $\Rightarrow$
:::info
如果 $P_i$ 有事件 $a$、 $P_j$ 有事件 $b$，那麼：
$a \Rightarrow b$ if and only if ((i) or (ii))
(i) $C_i(a) < C_j(b)$
(ii) $C_i(a) = C_j(b)$ and $P_i \prec P_j$
:::
也就是說 $\Rightarrow$ 是把 happened before ($\to$) 從偏序(partial ordering)推廣到全序(total ordering)的方法。

### Solving mutual exclusion problem
能夠全序定義事件對於分散式系統非常有用，以下舉例：
考慮有數個 process 的系統，所有系統共享一個資源，同時只能有一個 process 存取資源。
我們想要實做一個演算法來達成以下三點 Condition：
:::info
Condition
I. process 必須釋放資源才能給下個 process 用
II. 不同 request 必須依其產生的順序滿足(於 process 內產生的時間)
III. 如果每個允許過資源的 process 最終會釋出資源，那所有 request 最終都會被滿足。
:::
注意 **II** 並沒有註明 concurrent request 的處理方式。
如果是以一個中央管理 process 來給予資源，會按照 request 送到的順序來給予，那可能會違反 **II**。

這裡我們可以用 **IR1** 跟 **IR2** 來定義所有事件的全序($\Rightarrow$)。
這就只是確保所有 process 都知道其他 process 在幹嘛、進度到哪。

### Algorithm
每個 process 都會維護自己的 **request queue**。
一開始每個 **request queue** 都有一則訊息 "$T_0:P_0$ request resource" ，$P_0$ 是最一開始持有資源的，$T_0$ 為小於任何 clock 的初始值。
以下五個規則可以被定義：
:::info
1. 要要求資源時， $P_i$ 發送要求訊息 "$T_m:P_i$ request resource" 給其他所有 process，然後把該訊息放入自己的 **request queue**。$T_m$ 為時間戳記。
2. 當 $P_j$ 收到訊息 "$T_m:P_i$ request resource" 時，會把它放入自己的 **request queue** ，然後發送具時間戳記的答覆(acknowledgment)給 $P_i$。
3. 要釋放資源時， $P_i$ 從 **request queue** 中移除訊息，然後發送具時間戳記的 "$P_i$ release resource" 訊息給其他所有 process
4. 當 $P_j$ 收到 "$P_i$ release resource" 時，會把 "$T_m:P_i$ request resource" 從 **request queue** 中移除。
5. $P_i$ 可以拿到資源的條件如下:
    (i) 自己的 "$T_m:P_i$ request resource" 在 $\Rightarrow$ 規則排序下為最優先。
    (ii) 已經收到來自其他所有 process 具時間戳記大於 $T_m$ 的訊息。
:::
==**核心精神在於 "保證每個 process 在取得資源前已經充分了解其他 process 的訊息。**==


:::spoiler 滿足上面 Condition 的方法：
滿足 **Condition I**：因為 3. 跟 4. 是唯二從 **request queue** 刪除訊息的規則。
滿足 **Condition II**：上面 2. 保證了 $P_i$ 釋放資源後 5.(ii) 最終會被滿足。
滿足 **Condition III**：上面 3. 跟 4. 意味著如果每個有資源的 process 最後會釋放，那 5.(i) 最終會被滿足。
:::

## Anomalous Behavior
如果有外部事件介入，那就無法保證事件的順序一致。
例如 A 在電腦上發送事件 $a$，然後打電話給 B 叫他用電腦發送事件 $b$ ，那麼 $b$ 有可能拿到更低的時間戳記(非物理時間，而是 logical clock)，因為系統不知道打電話這個外部事件。

## Physical Clocks
解決上述外部事件的方法，可以導入物理時鐘。

假設 $C_i(t)$ 為 clock $C_i$ 於物理時間顯示 $t$ 時的數值。
- 一個物理時鐘應該為連續的(而非離散)，而且保值恆定速率，即 $\frac{dC_i(t)}{dt} \approx 1$ (速率接近每秒邁進1個數值)
- 每個物理時鐘的差距應該越小越好
:::info
**PC1** $\exists \kappa \ll 1$ such that $|dC_i(t)/dt-1| \lt \kappa$
**PC2** For all $i, j$: $|C_i(t)-C_j(t)| \lt \epsilon$
:::
通常石英鐘 $\kappa \le 10^{-6}$

設 $\mu$ 為系統傳遞訊息的最小時間，如果 $a\to b$ ，那麼如果 $a$ 發生於 $t$ ，則 $b$ 必然晚於 $t + \mu$ 發生。
所以要免疫外部事件介入，必須滿足
$\forall i, j, t$： $C_i(t+\mu)-C_j(t)>0$
再由 **PC1, PC2** 往下推導可得
$\epsilon/(1-\kappa)\lt\mu$

可以看到 process 之間距離越近($\mu$越小)，對時鐘精準度要求越高($\epsilon也要越小)$
==只要上面關係式成立，那麼外部事件介入也不會影響系統正確性==


## Conclusion
* happened before ($\to$) 定義了不變的偏序(partial ordering)來表達事件的關係。
* 延伸了 happened before ($\to$) 為全序(total ordering)(即$\Rightarrow$)，好定義一組演算法來解決同步問題。
* 為了免疫外部事件介入影響，定義了物理時鐘(physical clock)該有的限制來解決此問題。