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

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

Расширения Raft: pre-vote, learner, snapshot и линеаризуемые чтения

Суть Четыре критичных для production расширения — pre-vote против инфляции term, learner для безопасного масштабирования, snapshot для компрессии лога, ReadIndex и lease read для быстрых линеаризуемых запросов.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 16 min

Твой etcd-кластер имеет 50 000 чтений в секунду, но только 200 записей в секунду. Если каждое чтение требует раунда консенсуса, кластер тратит в 250 раз больше работы на чтения, чем на записи. Но если обслуживать чтения с stale-follower-а, теряется линеаризуемость. Как production Raft-реализации обслуживают чтения с sub-миллисекундной latency, не жертвуя корректностью?

Pre-vote: предотвращение паразитных выборов

Когда нода возвращается после долгого partition, она всё это время увеличивала счётчик term. Её RequestVote приходит с очень высоким term, вынуждая здоровые follower-ы обновить свои term и понизить текущего лидера — хотя никакого реального отказа не было. Кластер терпит ненужные выборы.

Pre-vote добавляет необязательный dry-run перед любыми реальными выборами. Прежде чем увеличить term, candidate шлёт сообщение PreVote. Ноды отвечают “я бы за тебя проголосовал” или “нет”, не меняя никакого персистентного состояния. Только если большинство ответило бы “да”, candidate реально увеличивает term и запускает настоящие выборы.

Цена: один дополнительный RPC round при реально нужных выборах. Выгода: ноль паразитных смен лидера от возвращающихся нод. Pre-vote теперь стандарт в etcd, TiKV и Hashicorp Raft.

Learner: безопасное масштабирование кластера

Добавлять новую ноду напрямую как voter опасно: она стартует с пустым логом, и её медленный catch-up деградирует commit latency при каждой записи до догонки. В 3-нодовом кластере добавление 4-го voter-а также временно снижает fault-tolerance (кворум теперь 3 из 4 вместо 2 из 3).

Learner-ы (не голосующие члены) решают это. Learner получает AppendEntries и реплицирует лог как follower, но не учитывается в кворумах и не голосует в выборах. Оператор промоутит learner-а в voter только после того, как лаг его лога снижается до порога (обычно под 1000 entry позади). Промоуция — это single-server membership change, безопасная и прозрачная.

Learner-ы также используются etcd, Consul и Yugabyte для замены нод после отказа: новая нода учится, догоняет, промоутится, затем мёртвая нода удаляется. Availability никогда не падает ниже исходного кворума в процессе замены.

Snapshot и log compaction

Лог Raft неограниченно растёт. Однолетний кластер с 10k записей/с записал 315 миллиардов entry — лог занял бы терабайты, а его воспроизведение после краша заняло бы дни.

Snapshot компрессирует лог: периодически каждая нода сериализует полное состояние state machine плюс тройку (lastIncludedIndex, lastIncludedTerm) на диск, затем удаляет все log entry до этого index. Если follower отстал настолько, что лидер уже скомпрессировал нужные ему entry, лидер шлёт RPC InstallSnapshot вместо пропущенных AppendEntries — follower грузит snapshot как начальное состояние, затем воспроизводит только недавний хвост лога.

СистемаДефолтный interval snapshot
etcdКаждые 10 000 entry
CockroachDBКаждые несколько секунд на range
TiKVНастраиваемый, дефолт 200 МБ

Частота snapshot — настраиваемый tradeoff: слишком часто амплифицирует fsync; слишком редко растит лог и замедляет recovery. Snapshot должен включать актуальную конфигурацию membership — забыть об этом классический баг в DIY Raft реализациях.

ReadIndex: линеаризуемые чтения без записи в лог

Наивный подход к линеаризуемым чтениям: лидер коммитит no-op log entry ради чтения, затем возвращает данные. Это сериализует чтения в лог — дорого (один полный раунд консенсуса на чтение).

ReadIndex устраняет запись в лог. При приходе чтения:

  1. Лидер запоминает текущий commitIndex.
  2. Лидер шлёт heartbeat большинству follower-ов, подтверждая, что он всё ещё активный лидер (предотвращает stale-лидера от обслуживания чтений после partition).
  3. Как только state machine лидера применяет entry до commitIndex, лидер возвращает результат.

Цена: один heartbeat round-trip (обычно 1–5 мс intra-region). Никакого нового log entry. Чтения масштабируются независимо от throughput записей.

Lease read: sub-миллисекундная latency

ReadIndex всё ещё платит за сетевой round-trip на каждое чтение. Lease read идёт дальше: лидер держит lease — временное окно, в котором он гарантированно является единственным лидером (потому что никакие выборы не могут завершиться в течение lease duration). В период lease лидер обслуживает чтения из своей текущей state machine без RPC.

Lease duration обычно election_timeout × 0.9 (например 9 мс при election timeout 10 мс), оставляя 10% буфер для clock skew.

Условие корректности: lease read безопасен только если clock skew между лидером и follower-ами ограничен. Если часы лидера бегут значительно быстрее follower-ов, lease может истечь у follower-а до того, как лидер думает — follower стартует новые выборы, пока старый лидер ещё обслуживает чтения. Поэтому NTP/PTP-синхронизация — требование корректности для lease read, а не просто гигиеническая практика.

Числа расширений Raft
fsync latency, NVMe + BBU
50–100 мкс
fsync latency, cloud SSD (EBS gp3)
1–3 мс
ReadIndex latency (intra-region)
1–5 мс
Lease read latency
менее 1 мс
Interval snapshot etcd (дефолт)
10 000 entry
TimeoutNow leadership transfer
менее 10 мс недоступности
Викторина

Почему lease read считается чувствительным к корректности, а ReadIndex — нет?

Викторина

Новая нода добавляется в 3-нодовый Raft-кластер напрямую как voter. Почему это может временно деградировать commit latency?

Вспомните перед уходом
  1. 01
    Нода возвращается после 30 минут offline. Без pre-vote что происходит с кластером?
  2. 02
    Какую информацию должен включать Raft snapshot помимо сериализованной state machine?
  3. 03
    ReadIndex описывается как требующий 'одного heartbeat round-trip на чтение'. Почему лидер не может пропустить это и сразу обслуживать чтения из своего текущего состояния?
Итог

Четыре расширения заполняют пробел между учебным Raft и production-системами. Pre-vote устраняет паразитные выборы, требуя dry-run до увеличения term. Learner-ы позволяют безопасно масштабироваться, давая новым нодам догнать лог до учёта в кворуме. Snapshot ограничивает рост лога, периодически создавая checkpoint state machine и позволяя InstallSnapshot для отстающих follower-ов. ReadIndex обеспечивает линеаризуемые чтения с одним heartbeat round-trip вместо полной записи в лог; lease read доводит это до sub-миллисекунды, ограничивая авторитет лидера по времени, но требует NTP-синхронизации как предусловия корректности. Все четыре стандартны в etcd, TiKV и Hashicorp Raft.

Связанные уроки
встречается в178
Продолжить восхождение ↑Raft в production: membership change, Multi-Raft и observability
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.