7.5 KiB
Pytanie 23: Zegary logiczne i wektory stempli czasowych
Pytanie
"Przedstawić koncepcję i przeznaczenie zegarów logicznych i wektorów stempli czasowych."
Przedmiot: ERSMS (Elementy Rozproszonych Systemów i Middleware'u Sieciowego)
📚 Odpowiedź główna
1. Problem czasu w systemach rozproszonych
┌─────────────────────────────────────────────────────────────────┐
│ Brak globalnego zegara: │
│ │
│ Node A: ──●────────●────────●──→ czas lokalny A │
│ e1 e2 e3 │
│ │↘ │
│ │ ↘ msg │
│ │ ↘ │
│ Node B: ────●─────────────●───→ czas lokalny B │
│ e4 e5 │
│ │
│ Pytanie: Czy e2 było przed e5, czy po? │
│ Zegary fizyczne: drift, synchronizacja niedokładna │
└─────────────────────────────────────────────────────────────────┘
Problem: Nie możemy polegać na zegarach fizycznych - drift, opóźnienia sieciowe, brak atomowej synchronizacji.
2. Relacja "happened-before" (Lamport)
Definicja (→)
Zdarzenie a happened-before b (a → b) jeśli:
- a i b są w tym samym procesie i a występuje przed b
- a to wysłanie wiadomości, b to jej odbiór
- Przechodniość: a → c ∧ c → b ⟹ a → b
P1: ─●─────●─────────●───→
a b d
│↘
│ ↘ m1
│ ↘
P2: ───────────●─────●───→
c e
a → b (ten sam proces)
b → c (wysłanie → odbiór)
a → c (przechodniość)
c → e (ten sam proces)
Zdarzenia współbieżne
Jeśli ¬(a → b) ∧ ¬(b → a), to a || b (współbieżne).
3. Zegar Lamporta (Scalar Clock)
Algorytm
Każdy proces P_i ma licznik C_i:
1. Przed każdym zdarzeniem lokalnym:
C_i := C_i + 1
2. Wysyłając wiadomość m:
C_i := C_i + 1
Dołącz timestamp(m) = C_i
3. Odbierając wiadomość m z timestamp t:
C_i := max(C_i, t) + 1
Przykład
P1: C=0 ──●(1)────●(2)─────────●(5)──→
a b d
│↘
│ ↘ m(2)
│ ↘
P2: C=0 ────●(1)───────●(3)────●(4)──→
x c e
Zdarzenia: a=1, b=2, x=1, c=3, e=4, d=5
Właściwości
| Właściwość | Zegar Lamporta |
|---|---|
| a → b ⟹ C(a) < C(b) | ✅ TAK |
| C(a) < C(b) ⟹ a → b | ❌ NIE |
Ograniczenie: C(a) < C(b) nie oznacza, że a → b (mogą być współbieżne).
4. Zegary wektorowe (Vector Clocks)
Algorytm
Każdy z N procesów ma wektor V[1..N]:
1. Przed każdym zdarzeniem lokalnym:
V_i[i] := V_i[i] + 1
2. Wysyłając wiadomość m:
V_i[i] := V_i[i] + 1
Dołącz timestamp(m) = V_i
3. Odbierając wiadomość m z timestamp T:
V_i[j] := max(V_i[j], T[j]) dla każdego j
V_i[i] := V_i[i] + 1
Przykład
P1: [0,0,0] ──●[1,0,0]────●[2,0,0]─────────●[3,2,0]──→
a b d
│↘
│ ↘ m[2,0,0]
│ ↘
P2: [0,0,0] ────●[0,1,0]─────────●[2,2,0]────●[2,3,0]──→
x c e
Porównanie wektorów
V ≤ W ⟺ ∀i: V[i] ≤ W[i]
V < W ⟺ V ≤ W ∧ V ≠ W
V || W ⟺ ¬(V ≤ W) ∧ ¬(W ≤ V)
Przykład:
[2,3,1] < [2,4,1] (happened-before)
[2,3,1] || [1,4,2] (współbieżne)
Właściwości
| Właściwość | Zegar wektorowy |
|---|---|
| a → b ⟺ V(a) < V(b) | ✅ TAK |
| Wykrycie współbieżności | ✅ TAK |
Zegary wektorowe charakteryzują dokładnie relację happened-before!
5. Porównanie
| Cecha | Lamport | Vector Clock |
|---|---|---|
| Rozmiar | O(1) | O(N) |
| a → b ⟹ C(a) < C(b) | ✅ | ✅ |
| C(a) < C(b) ⟹ a → b | ❌ | ✅ |
| Wykrycie współbieżności | ❌ | ✅ |
| Zastosowanie | Uporządkowanie | Wykrywanie konfliktów |
6. Zastosowania
| Zastosowanie | Typ zegara | Cel |
|---|---|---|
| Mutual exclusion | Lamport | Uporządkowanie żądań |
| Causal broadcast | Vector | Dostarczanie w kolejności przyczynowej |
| Conflict detection | Vector | Wykrycie współbieżnych zapisów |
| Distributed debugging | Vector | Odtworzenie kolejności zdarzeń |
| Version vectors (Dynamo) | Vector | Wykrycie konfliktów w replikacji |
Przykład: Dynamo/Riak
Put(key, value) z vector clock:
Client → Server A: put("x", "v1", [A:1])
Client → Server B: put("x", "v2", [B:1])
Konflikt wykryty: [A:1] || [B:1]
Oba wartości przechowane, klient rozwiązuje
7. Warianty i rozszerzenia
| Wariant | Opis |
|---|---|
| Interval Tree Clocks | Dynamiczna liczba procesów |
| Bloom Clocks | Probabilistyczne, kompaktowe |
| Hybrid Logical Clocks | Lamport + czas fizyczny |
| Matrix Clocks | Wiedza o wiedzy innych |
🧠 Mnemoniki
"Lamport = Licznik skalarny":
Jedna liczba, nie wykrywa współbieżności
"Vector = Każdy wie o każdym":
V[i] = ile zdarzeń widział proces i
"< oznacza →, || oznacza współbieżność":
Porównanie wektorów = relacja przyczynowa
❓ Pytania dodatkowe
Q1: "Dlaczego nie używać NTP zamiast zegarów logicznych?"
Odpowiedź: NTP ma błąd ~ms (LAN) do ~100ms (WAN). Dla zdarzeń szybszych niż błąd synchronizacji, kolejność fizyczna jest nieznana. Zegary logiczne dają gwarancje przyczynowe bez względu na drift.
Q2: "Co to jest causal consistency?"
Odpowiedź: Model spójności gdzie zapisy przyczynowo zależne są widziane w tej samej kolejności przez wszystkich. Wymaga vector clocks. Słabszy niż sequential consistency, silniejszy niż eventual.
Q3: "Problem skalowalności vector clocks?"
Odpowiedź: O(N) rozmiar dla N procesów. Rozwiązania: przycinanie (Dynamo), Interval Tree Clocks (dynamiczne ID), Bloom clocks (probabilistyczne).
🎯 Kluczowe punkty
- Problem: Brak globalnego zegara w systemach rozproszonych
- Lamport: Skalarny, a → b ⟹ C(a) < C(b), nie odwrotnie
- Vector: a → b ⟺ V(a) < V(b), wykrywa współbieżność
- Zastosowania: Replikacja, debugging, causal broadcast
📖 Źródła
- Lamport - "Time, Clocks, and the Ordering of Events" (1978)
- Fidge, Mattern - "Vector Clocks" (1988)
- DeCandia et al. - "Dynamo" (Amazon, 2007)