awesome-everything EN
↑ Обратно к восхождению

Распределённые системы

Часы: чтение кода и трасс

Суть Читай код обновления часов и трассы событий — инкремент Lamport, merge vector clock, сравнение happens-before и детекцию конкурентности — и предсказывай результат.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Логические часы — это крошечные куски кода с тонкими правилами корректности. Прочитай каждую процедуру обновления и трассу событий, затем выбери ответ, который senior-инженер защитит на ревью — где на самом деле живёт баг, порядок или вердикт о конкурентности.

Цель

Отработай базовые операции логических часов в коде: инкремент Lamport на отправку и приём, merge vector clock, сравнение happens-before и детекцию того, что два события конкурентны, а не упорядочены.

Сниппет 1 — приём Lamport

class LamportClock:
    def __init__(self):
        self.t = 0

    def on_local_event(self):
        self.t += 1
        return self.t

    def on_send(self):
        self.t += 1
        return self.t            # piggyback self.t on the message

    def on_receive(self, msg_t):
        self.t = self.t          # <-- BUG candidate
        self.t += 1
        return self.t
Викторина

Обработчик приёма игнорирует msg_t. Какой инвариант это ломает и каков фикс?

Сниппет 2 — merge vector clock

def merge_receive(local, msg_vec, my_id):
    # local and msg_vec are dicts: node_id -> counter
    merged = {}
    for node in set(local) | set(msg_vec):
        merged[node] = max(local.get(node, 0), msg_vec.get(node, 0))
    merged[my_id] += 1
    return merged
Викторина

Этот merge при приёме корректен. Какое утверждение объясняет, почему поэлементный max плюс один self-инкремент — ровно то, что нужно?

Сниппет 3 — сравнение двух vector clock

def compare(a, b):
    # returns 'before', 'after', or 'concurrent'
    le = all(a.get(k, 0) <= b.get(k, 0) for k in set(a) | set(b))
    ge = all(a.get(k, 0) >= b.get(k, 0) for k in set(a) | set(b))
    if le and not ge: return 'before'      # a happens-before b
    if ge and not le: return 'after'
    if le and ge:     return 'equal'
    return 'concurrent'
A = {n1: 2, n2: 1, n3: 0}
B = {n1: 1, n2: 1, n3: 4}
Викторина

Что вернёт compare(A, B) для этих двух векторов и что это значит для разрешения конфликтов?

Сниппет 4 — трасса событий

P1: a(send m1)            Lamport: a=1
P2: b(recv m1) -> c(send m2)
P3: d(recv m2)
P1: e(local)              Lamport: e=2
Викторина

Используя happens-before по этой трассе, каково отношение между e на P1 и d на P3?

Итог

Операции с часами малы, но беспощадны: приём Lamport обязан брать max(local, msg) + 1, иначе может проштамповать receive раньше его send; приём vector clock берёт поэлементный max и бампит только свою компоненту; сравнение векторов покомпонентно, и когда каждая сторона впереди по какому-то узлу — события concurrent и должны держаться как siblings. И никакое числовое упорядочивание Lamport не влечёт причинности без связующего пути сообщений. Читай код и трассу, затем решай порядок vs конкурентность по причинной структуре, а не по сырым целым числам.

Продолжить восхождение ↑Часы: построй хранилище с детекцией конфликтов
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.