АрхитектураОблако и инфраструктураИнженер по платформенной инфраструктуре

У нескольких реплик контроллера есть общая задача, которую одновременно должна выполнять только одна. Как м...

У нескольких реплик контроллера есть общая задача, которую одновременно должна выполнять только одна. Как механизм распределённого лидерства предотвращает параллельное выполнение этой задачи?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Распределённое лидерство выбирает одну реплику владельцем общей блокировки с ограниченным сроком действия — lease. Лидер регулярно продлевает её, а остальные реплики наблюдают за состоянием; если срок истёк, одна из них атомарно захватывает lease и становится новым лидером.

Это предотвращает нормальную конкурентную работу нескольких реплик, но само по себе не даёт строгой гарантии exactly-once: остановившаяся или изолированная реплика может временно продолжать работу. Для критичных операций дополнительно требуется механизм fencing.

Исторический контекст

Подход появился как решение проблемы запуска одного активного экземпляра сервиса при наличии нескольких реплик. Несколько экземпляров повышают отказоустойчивость, но без координации они могут одновременно выполнять одну задачу: планировать один и тот же job, менять общий ресурс или публиковать конфликтующие результаты.

В распределённой системе нельзя надёжно отличить остановившийся процесс от временно недоступного узла. Поэтому вместо постоянной блокировки используют аренду с TTL: право лидерства нужно периодически подтверждать, иначе оно автоматически освобождается.

Постановка проблемы

Предположим, три реплики контроллера обрабатывают очередь заданий, причём каждое задание должно быть запущено только одним контроллером. Если все реплики считают себя активными, одно задание может выполниться несколько раз, а изменения общего состояния — конфликтовать.

Нельзя просто выбрать лидера при старте и навсегда сохранить этот выбор. Лидер может завершиться, потерять сетевую связность или перестать отвечать, поэтому система должна передать роль другой реплике, не создавая длительного простоя.

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

Подробное решение

Обычно реплики используют общий согласованный объект координации: например, Lease в Kubernetes или запись в системе распределённого хранения. В объекте фиксируются идентификатор текущего владельца, время последнего продления и длительность аренды.

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

Лидер периодически продлевает аренду. Потеря процесса, зависание или длительная недоступность API приводят к тому, что продление прекращается; после истечения TTL другая реплика получает возможность стать лидером.

Важна разница между эксклюзивностью записи lease и фактической остановкой старого лидера. Если старый лидер потерял связь с хранилищем, но продолжает выполнять работу, новый лидер может уже получить lease. Поэтому лидер должен прекращать опасные операции при невозможности продлить аренду, а критичные внешние системы должны проверять маркер поколения или другой fencing-токен.

Параметры аренды требуют компромисса. Большой TTL уменьшает вероятность ложной смены лидера при кратковременных задержках, но увеличивает время восстановления после отказа. Малый TTL ускоряет переключение, однако повышает риск потери лидерства из-за перегрузки, сетевых задержек или временной недоступности хранилища.

Нужно также учитывать часы. Если логика опирается на локальное время, существенный clock skew способен нарушить ожидания. Обычно применяют монотонные интервалы там, где это возможно, задают запас по тайм-аутам и считают потерю возможности продления достаточным основанием для прекращения активной работы.

Лидерство не заменяет идемпотентность. Задание может быть принято старым лидером перед отказом, а затем повторно обработано новым. Операции должны безопасно повторяться либо использовать дедупликацию, уникальные идентификаторы заданий и транзакционные гарантии внешнего хранилища.

Ситуация из практики

В кластере работают три реплики контроллера, который создаёт резервные копии баз данных по расписанию. Изначально каждая реплика самостоятельно читала расписание, поэтому после сетевого сбоя две реплики одновременно запускали одну резервную копию, создавая лишнюю нагрузку на базу.

Рассматривались три варианта. Жёстко закрепить контроллер за одним узлом было просто, но это создавало единую точку отказа. Использовать внешнюю очередь с уникальным потребителем давало хорошее распределение заданий, но требовало менять модель обработки и не решало выбор единственного планировщика. Выбрать лидера через lease оказалось наименее затратным изменением: только лидер вычислял расписание, а остальные были горячим резервом.

После внедрения TTL в 30 секунд и периода продления в 10 секунд отказ лидера стал приводить к переключению примерно за один интервал аренды. Запуск резервной копии сделали идемпотентным по идентификатору задания, а база дополнительно отклоняла повторную постановку уже выполняемого задания. Это закрыло как проблему параллельных лидеров, так и возможный повтор после сбоя между запуском и фиксацией результата.

Что кандидаты часто упускают

  1. Достаточно ли lease, чтобы гарантировать выполнение задачи ровно один раз?

Нет. Lease ограничивает владение ролью, но не делает бизнес-операцию атомарной. Старый лидер может успеть начать операцию до потери lease, а новый — начать её повторно. Для нужной гарантии применяют идемпотентность, дедупликацию, транзакции или fencing-токен, который внешний ресурс проверяет перед изменением.

  1. Что произойдёт при временном разделении сети между лидером и хранилищем координации?

Старый лидер не сможет продлить lease, поэтому после TTL другая реплика может стать лидером. Если старый процесс продолжит выполнять работу, возникнет split-brain на уровне бизнес-операций. Корректная реализация должна прекращать опасные действия при потере подтверждения лидерства; для особенно критичных ресурсов нужен внешний fencing, например проверка актуального поколения владельца.

  1. Как выбрать TTL и интервал продления?

TTL должен быть заметно больше нормального времени сетевого запроса, задержки хранилища и возможной кратковременной нагрузки, иначе здоровый лидер будет часто терять роль. Интервал продления обычно делают существенно меньше TTL, чтобы оставался запас на несколько неудачных попыток. Слишком большой TTL ухудшает время восстановления, а слишком маленький увеличивает ложные перевыборы и нагрузку на систему координации.