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

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

Выбор лидера: чтение кода и логов

Суть Читай реальные сниппеты лока, lease и fencing-токена плюс лог split-brain, предскажи поведение и выбери фикс, который сеньор сделает первым.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min

Баги выбора лидера прячутся в зазоре между «я держу лок» и «моя запись приземлилась». Читай код и лог, затем выбери фикс, который закрывает зазор, — а не тот, что лишь сужает его.

Цель

Отработай петлю, которую ты запускаешь в каждом инциденте координации: прочитай путь лока/lease, найди момент, где пауза или partition может вставить второго писателя, и потянись за фиксом на стороне ресурса раньше, чем за подкруткой таймаута.

Сниппет 1 — захват, затем запись

func runJob(lock *LockService, store *ObjectStore) error {
    if err := lock.Acquire("job-leader", 10*time.Second); err != nil {
        return err // лидирует кто-то другой
    }
    defer lock.Release("job-leader")

    // ... минуты работы, включая возможную долгую GC-паузу ...
    return store.Put("result.csv", data) // запись в общее хранилище
}
Викторина

Лок корректен и эксклюзивен. Почему это всё равно может породить двух одновременных писателей в result.csv?

Сниппет 2 — добавляем fencing-токен

func runJob(lock *LockService, store *ObjectStore) error {
    token, err := lock.Acquire("job-leader", 10*time.Second) // возвращает монотонный токен
    if err != nil {
        return err
    }
    defer lock.Release("job-leader")

    // ... работа, возможная пауза ...
    return store.Put("result.csv", data, token) // токен передан в запись
}
Викторина

Токен теперь проброшен в store.Put. При каком условии это реально останавливает устаревшую запись — и что обязан делать store.Put?

Сниппет 3 — петля keep-alive для lease

func keepLeadership(c *etcd.Client, leaseID etcd.LeaseID, onLost func()) {
    ka, _ := c.KeepAlive(ctx, leaseID) // канал подтверждений продления
    for {
        select {
        case resp, ok := <-ka:
            if !ok {                 // канал закрыт = продление сорвалось
                onLost()             // мы больше не лидер
                return
            }
            _ = resp
        }
    }
}
Викторина

onLost() срабатывает, когда продления keep-alive срываются. Почему действие по onLost необходимо, но НЕ достаточно для безопасности?

Сниппет 4 — лог split-brain

12:00:01  leaderA  acquired lease (term=7, token=33), writing batch
12:00:04  leaderA  >>> stop-the-world GC pause begins
12:00:11  coord    leaseA expired (no keep-alive 10s); electing
12:00:11  coord    leaderB won (term=8, token=34)
12:00:11  leaderB  acquired lease, store accepted write token=34
12:00:18  leaderA  <<< GC pause ends (14s)
12:00:18  leaderA  store.Put(result.csv, token=33) -> REJECTED (highest=34)
Викторина

Читая этот лог, какое утверждение — верное прочтение сеньора?

Итог

Каждый баг выбора лидера читается одинаково: лок или lease гарантирует исключение лишь пока держатель исполняется, поэтому пауза между «захватом» и «записью» позволяет смещённому лидеру проснуться и записать устаревшие данные. Проброс монотонного fencing-токена в запись необходим, но именно РЕСУРС, отвергающий любой токен ниже своего наибольшего, реально навязывает безопасность. Колбэки keep-alive и более короткие TTL не помогут процессу, который не исполняется. Диагностируй по логу, найди зазор паузы-или-partition и чини его там, где приземляется запись, — а не там, где выдан лок.

Продолжить восхождение ↑Выбор лидера: собери отсечённого единственного писателя
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.