Вибір лідера
1) Навіщо потрібен лідер і коли він взагалі виправданий
Лідер - вузол, що має ексклюзивне право виконувати критичні дії: запуск крона/ETL, координація шардів, розподіл ключів, зміна конфігурації. Він спрощує інваріанти («один виконавець»), але додає ризики (SPOF, пере-вибори, лаг).
Використовуйте лідерство, якщо:- потрібна єдиність виконання (наприклад, білінг-агрегатор раз на хвилину);
- потрібна серилізація змін (реєстр конфігурацій, розподілені блокування);
- кластерний протокол передбачає лідерську реплікацію (Raft).
- проблему вирішує ідемпотентність і порядок за ключем;
- можна розпаралелити через work-stealing/черги;
- «лідер» стає єдиною вузькою точкою (широкий фан-ін).
2) Базова модель: lease + кворум + епоха
Терміни
Lease (оренда): лідер отримує право на T секунд; зобов'язаний продовжувати.
Heartbeat: періодичне продовження/сигнал «живий».
Epoch/term (епоха, термін): монотонно зростаючий номер лідерства. Допомагає розпізнати «старих» лідерів.
Fencing token: той же монотонний номер, який перевіряє споживач ресурсу (БД/сховище) і відкидає операції старого лідера.
Інваріанти
У будь-який момент не більше одного дійсного лідера (safety).
При збої можливий прогрес: за розумний час обирається новий (liveness).
Операції лідера супроводжуються епохою; синці приймають тільки новіші епохи.
3) Огляд алгоритмів і протоколів
3. 1 Raft (лідерська реплікація)
Стани: Follower → Candidate → Leader.
Таймери: random election timeout (джиттер), RequestVote; лідер тримає AppendEntries як heartbeat.
Гарантії: кворум, відсутність split-brain при стандартних передумовах, журнал з логічною монотонією (term/index).
3. 2 Paxos/Single-Decree / Multi-Paxos
Теоретична основа консенсусу; на практиці - варіації (e. g., Multi-Paxos) з «обраним координатором» (аналог лідера).
Складніше для прямої реалізації; частіше використовуються готові реалізації/бібліотеки.
3. 3 ZAB (ZooKeeper Atomic Broadcast)
Механізм ZK: лідерська реплікація журналу з фазами відновлення; епохи (zxid) і послідовні ефемерні вузли для примітивів на зразок лідерства.
3. 4 Bully/Chang-Roberts (кільця/монарх)
«Навчальні» алгоритми для статичних топологій без кворуму. Не враховують часткові збої/розділення мережі - не застосовуйте в проді.
4) Практичні платформи
4. 1 ZooKeeper
Патерн EPHEMERAL_SEQUENTIAL: процес створює '/leader/lock-XXXX', мінімальний номер - лідер.
Втрата сесії ⇒ вузол зникає ⇒ пере-вибір миттєвий.
Справедливість через очікування «попередника».
4. 2 etcd (Raft)
Нативне лідерство на рівні самого кластера; для додатків - etcd concurrency: `Session + Mutex/Election`.
Lease-ID с TTL, keepalive; можна зберігати епоху в значенні ключа.
4. 3 Consul
`session` + `KV acquire`: хто утримує ключ - той і лідер. TTL/серцебиття на сесії.
4. 4 Kubernetes
Leases coordination API (`coordination. k8s. io/v1`): ресурс `Lease` c `holderIdentity`, `leaseDurationSeconds`, `renewTime`.
Клієнтська бібліотека'leaderelection'( client-go) реалізує захоплення/продовження; ідеальна для лідера-подів.
5) Як побудувати «безпечного» лідера
5. 1 Зберігайте епоху і fencing
Кожне лідерство збільшує epoch (наприклад, ревізія etcd/ZK zxid або окремий лічильник).
Всі побічні ефекти лідера (запис в БД, виконання завдань) зобов'язані передавати «epoch» і порівнюватися:sql
UPDATE cron_state
SET last_run = now(), last_epoch =:epoch
WHERE name = 'daily-rollup' AND:epoch > last_epoch;
Старий лідер (після split-brain) буде відкинутий.
5. 2 Таймінги
'leaseDuration'≥'2-3 × heartbeatInterval + мережа + p99 GC-пауза'.
Election timeout - рандомізувати (джиттер), щоб кандидати не колізилися.
При втраті продовження - негайно припинити критичні операції.
5. 3 Ідентичність
`holderId = node#pid#startTime#rand`. При оновленні/знятті звіряйте той же holder.
5. 4 Спостерігачі (watchers)
Всі послідовники підписані на зміни «Lease/Election» і починають/зупиняють роботу відповідно до стану.
6) Реалізації: фрагменти
6. 1 Kubernetes (Go)
go import "k8s. io/client-go/tools/leaderelection"
lec:= leaderelection. LeaderElectionConfig{
Lock: &rl. LeaseLock{
LeaseMeta: metav1. ObjectMeta{Name: "jobs-leader", Namespace: "prod"},
Client: coordClient,
LockConfig: rl. ResourceLockConfig{Identity: podName},
},
LeaseDuration: 15 time. Second,
RenewDeadline: 10 time. Second,
RetryPeriod: 2 time. Second,
Callbacks: leaderelection. LeaderCallbacks{
OnStartedLeading: func(ctx context. Context) { runLeader(ctx) },
OnStoppedLeading: func() { stopLeader() },
},
}
leaderelection. RunOrDie(context. Background(), lec)
6. 2 etcd (Go)
go cli, _:= clientv3. New(...)
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
e:= concurrency. NewElection(sess, "/election/rollup")
_ = e. Campaign (ctx, podID )//blocking call epoch: = sess. Lease ()//use as part of fencing defer e. Resign(ctx)
6. 3 ZooKeeper (Java, Curator)
java
LeaderSelector selector = new LeaderSelector(client, "/leaders/rollup", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership () performs leader work with try/finally
7) Пере-вибори і деградація сервісу
Різкі флаппінги лідера → «риб'яча кісточка» в графіках. Лікується збільшенням leaseDuration/renewDeadline і усуненням GC/CPU-пил.
На період пере-вибору включайте brownout: знижуйте інтенсивність фонових завдань або повністю заморожуйте їх до підтвердженого leadership.
Для довгих джобів робіть чекпоінти + ідемпотентний докат після зміни лідера.
8) Split-brain: Як не потрапити
Використовуйте CP-сховища (etcd/ZK/Consul) з кворумом; без кворуму лідера взяти не можна.
Ніколи не будуєте лідерство на AP-кеші без арбітра кворуму.
Навіть в CP-моделі тримайте fencing на рівні ресурсу - це страховка від рідкісних позаштатних сценаріїв (паузи, підвислі драйвери).
9) Спостережуваність і експлуатація
Метрики
`leadership_is_leader{app}` (gauge 0/1).
`election_total{result=won|lost|resign}`.
`lease_renew_latency_ms{p50,p95,p99}`, `lease_renew_fail_total`.
'epoch _ value'( монотонність за кластерами).
'flaps _ total'- число змін лідера за вікно.
Для ZK/etcd: лаг реплікації, здоров'я кворуму.
Алерти
Часта зміна лідера (> N за годину).
Провали продовження'renew '/високий p99.
Незхідність epoch (дві різні епохи в різних вузлах).
Немає лідера довше X сек (якщо бізнес не допускає).
Логи/трейси
Лінкуйте події: `epoch`, `holderId`, `reason` (lost lease, session expired), `duration_ms`.
10) Тест-плейбуки (Game Days)
Partition: розірвіть мережу між 2 зонами - лідерство допускається тільки в кворумній частині.
GC-stop: штучно зупиніть лідер на 5-10с - повинен втратити оренду і припинити роботу.
Clock skew/drift: переконайтеся, що коректність не залежить від wall-clock (fencing/epoch рятують).
Kill -9: раптовий креш лідера → новий лідер за ≤ leaseDuration.
Slow storage: сповільніть диски/лог Raft - оцініть час виборів, налагодьте таймінги.
11) Анти-патерни
«Лідер» через Redis'SET NX PX'без fencing і без кворуму.
'leaseDuration'менше p99 тривалості критичної операції.
Зупинка/продовження роботи після втрати лідерства («ще хвилинку дороблю»).
Відсутність джиттера в election-таймерах → шторм виборів.
Єдина довга джоба без чекпоінтів - кожен флап призводить до повтору з нуля.
Тісний зв'язок лідерства і маршрутизації трафіку (sticky) без fallback - поди при флапі отримують 5xx.
12) Чек-лист впровадження
- Обраний арбітр кворуму: etcd/ZK/Consul/K8s Lease.
- Зберігаємо і передаємо epoch/fencing у всі побічні ефекти лідера.
- Налаштовані таймінги: 'leaseDuration','renewDeadline','retryPeriod'з запасом на мережу/GC.
- Вбудовані watchers і коректна зупинка робіт при втраті лідерства.
- Лідерські завдання ідемпотентні і чекпоінтяться.
- Метрики/алерти і логування'epoch/holderId'включені.
- Проведені game days: partition, GC-stop, kill, clock skew.
- Документовані політики: хто/що робить лідер, хто може його замінити, як резолвити конфлікти epoch.
- План деградації: що робить система без лідера.
- Тест продуктивності: flaps під навантаженням не руйнують SLO.
13) FAQ
Q: Чи можна лідерство будувати без кворуму?
A: У проді - ні. Потрібен CP-компонент (кворум) або хмарний сервіс з еквівалентними гарантіями.
Q: Навіщо epoch, якщо є lease?
A: Lease забезпечує живучість, але не захищає від «старого лідера» після поділу/пауз. Epoch/fencing робить ефекти старого лідера недійсними.
Q: Які дефолти таймінгів в K8s?
A: Часто використовують «LeaseDuration≈15s», «RenewDeadline≈10s», «RetryPeriod≈2s». Підберіть під вашу p99-навантаження і GC.
Q: Як тестувати лідерство локально?
A: Запустіть 3-5 інстансів, емулюйте мережу (tc/netem), паузи (SIGSTOP), вбивайте лідера (SIGKILL), перевіряйте метрики/логи/епохи.
Q: Що робити з довгими завданнями при зміні лідера?
A: Чекпоінт + ідемпотентний докат; при втраті лідерства - негайний стоп і звільнення ресурсів.
14) Підсумки
Надійний вибір лідера - це кворумний арбітр + дисципліна епох. Тримайте лідерство як оренду з heartbeat, бийте всі ефекти fencing-токеном, налаштовуйте таймінги з запасом, робіть завдання лідера ідемпотентними і спостережуваними, регулярно програвайте збої. Тоді «один і тільки один» виконавець буде не гаслом, а гарантією, стійкою до паузи, мережевих примх і людських помилок.