Устройство механизма конфигурации V2

Конфигурация V2 в YDB реализует единый подход к управлению настройками кластера. Как пользоваться этим механизмом, описано в разделе для DevOps, а эта статья сфокусирована именно на его техническом устройстве. Она будет полезна разработчикам и контрибьюторам YDB, желающим внести изменения в этот механизм, а также всем, кто хочет более глубоко разобраться в происходящем на кластере YDB при изменении конфигурации.

Конфигурация кластера YDB хранится в нескольких местах и различные компоненты кластера координируют синхронизацию между ними:

  • в виде набора файлов конфигурации узла в файловой системе каждого узла YDB (необходимы для использования в момент запуска узла для подключения к остальному кластеру);
  • в специальной области хранения метаданных на каждом PDisk (кворум из PDisk считается единственным источником правды о конфигурации);
  • в локальной базе таблетки DS Controller (для нужд распределённого хранилища);
  • в локальной базе таблетки Console.

Часть параметров вступает в силу на узле сразу после того, как изменённая конфигурация будет доставлена туда, а часть — только после перезапуска узла.

Распределённая система конфигурации (Distconf)

Distconf — это система управления конфигурацией V2 кластера YDB, основанная на Node warden, узлах хранения и их PDisk'ах. Все изменения конфигурации V2, включая первоначальную инициализацию, проходят через неё.

Роль лидера

Центральным элементом системы Distconf является лидер — единственный узел в кластере, который на текущий момент имеет право инициировать и координировать изменения конфигурации. Наличие единой точки принятия решений устраняет гонки и конфликты, гарантируя последовательное применение изменений. Если текущий лидер выходит из строя, кластер автоматически запускает процесс выборов нового лидера. Далее в этом разделе мы рассмотрим, как именно происходят выборы и как лидер управляет кластером.

Кворум и источник правды

Ключевым элементом надёжности Distconf является хранение конфигурации непосредственно на PDisk'ах. Любое изменение считается успешно применённым только тогда, когда оно записано в область метаданных на кворуме дисков. Это означает, что даже при выходе из строя части узлов или дисков система сможет восстановить актуальную и целостную конфигурацию, прочитав её с оставшихся. Именно кворумное хранилище на PDisk'ах, а не состояние какого-либо одного узла, является единственным источником правды о конфигурации кластера. Этот механизм реализован в distconf_persistent_storage.cpp.

Стоит отметить, что запись в область метаданных PDisk'а для Distconf — это специальная низкоуровневая операция, которая происходит напрямую через NodeWarden и не использует стандартный путь работы с данными через DSProxy.

Сам механизм кворума является иерархическим и реализован в distconf_quorum.h. Базовый принцип — «большинство из большинств». Для принятия решения (например, о применении новой конфигурации) требуется:

  1. Наличие большинства ответивших дисков внутри каждого дата-центра. Дата-центр, выполнивший это условие, считается «работоспособным».
  2. Наличие большинства «работоспособных» дата-центров в кластере.

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

Процесс Binding и выборы лидера

Выборы лидера в Distconf осуществляются в процессе построения связей (Binding), описанном в distconf_binding.cpp.

  1. Обнаружение: Узлы хранения получают полный список статических узлов кластера через стандартный механизм TEvInterconnect::TEvNodesInfo.
  2. Построение дерева связей: Каждый узел инициирует подключение (bind) к случайному узлу из общего списка, которому он ещё не подчинён. Этот процесс формирует ациклический граф, который на промежуточных стадиях представляет собой лес (набор несвязанных деревьев), а в конечном итоге — единое дерево, покрывающее все узлы.
  3. Предотвращение циклов и обмен топологией: Механизм защиты от циклов основан на постоянном обмене информацией о текущей топологии. Когда узел А пытается подключиться к узлу Б, он отправляет Б всё своё известное поддерево. Узел Б отклоняет запрос, если А уже является его потомком. В случае одновременной попытки взаимного подключения конфликт разрешается в пользу узла с большим NodeId.
  4. Определение лидера: В процессе слияния деревьев неизбежно остаётся один узел, который не может ни к кому подключиться, потому что все остальные узлы уже находятся в его поддереве. Этот узел становится корнем итогового дерева и объявляется лидером.

Если текущий лидер выходит из строя, процесс Binding запускается заново, и кластер выбирает нового лидера.

Scatter/Gather — механизм распространения команд

Для управления кластером лидер использует механизм Scatter/Gather (distconf_scatter_gather.cpp), который работает поверх дерева, построенного в процессе Binding.

  • Scatter (рассылка)
    Когда лидеру нужно отдать команду всем узлам (например, предложить новую конфигурацию), он отправляет её своим прямым потомкам в дереве. Каждый узел, получив команду, ретранслирует её своим потомкам. Так команда эффективно распространяется по всему дереву до самых листьев.
  • Gather (сбор)
    После выполнения команды узлы должны отчитаться о результате. Ответы собираются в обратном порядке: листья отправляют результаты своим родителям, те, в свою очередь, агрегируют их и отправляют выше. В итоге лидер получает обобщённый результат от всего кластера.

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

Конечный автомат (FSM) и жизненный цикл изменений

Весь процесс изменения конфигурации на лидере управляется конечным автоматом (finite-state machine или FSM), реализованным в distconf_fsm.cpp. FSM гарантирует, что в один момент времени выполняется только одна операция изменения конфигурации, предотвращая гонки и конфликты.

Когда поступает запрос на изменение, FSM переходит в состояние IN_PROGRESS, блокируя новые запросы.

Лидер использует Scatter/Gather для выполнения двухфазного коммита: сначала рассылает предложение (Propose), а после получения кворума подтверждений — команду на применение (Commit).

После успешного завершения или отмены операции FSM возвращается в состояние RELAX, разрешая обработку следующих запросов.

Управление конфигурацией через InvokeOnRoot

Запросы TEvNodeConfigInvokeOnRoot представляют собой унифицированный механизм для любых изменений конфигурации кластера. Эти команды могут быть инициированы как администратором системы (например, через CLI), так и другими компонентами YDB в автоматическом режиме (например, таблеткой BlobStorageController в процессе Self-Heal).

Независимо от источника, любой такой запрос обрабатывается по единому сценарию:

  1. Запрос доставляется на узел-лидер Distconf (если был отправлен не на него, то перенаправляется автоматически).
  2. Лидер запускает FSM и выполняет процесс Scatter/Gather, как описано выше.

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

Основные команды, поддерживаемые этим механизмом:

  • UpdateConfig: Внести частичные изменения в текущую конфигурацию. Изменения передаются в виде protobuf-сообщения TStorageConfig.
  • QueryConfig: Запросить текущую и предлагаемую конфигурацию. Ответ содержит protobuf-сообщения TStorageConfig.
  • ReplaceStorageConfig: Заменить текущую конфигурацию новой, переданной в виде YAML.
  • FetchStorageConfig: Вернуть загруженную YAML конфигурацию кластера.
  • ReassignGroupDisk: Заменить диск в статической группе хранения.
  • StaticVDiskSlain: Обработать событие выхода из строя VDisk'а в статической группе.
  • DropDonor: Удалить диски-доноры после завершения миграции данных.
  • BootstrapCluster: Инициировать первоначальное создание кластера.

Интеграция и дополнительные механизмы

Помимо основных процессов, Distconf тесно взаимодействует с другими компонентами системы:

  • Контроллер распределённого хранилища

    DS-controller получает от Distconf изменения в конфигурации и использует их для работы распределённого хранилища.

  • Узлы баз данных

    Узлы баз данных (database nodes) подписываются на события TEvNodeWardenDynamicConfigPush, чтобы получать обновления конфигурации в реальном времени.

  • Self-Heal

    При использовании Distconf для статической группы работает Self-Heal по аналогии с динамическими группами.

  • Локальные YAML-файлы на узлах

    Хранятся в директории, указанной аргументом запуска сервера ydbd --config-dir, и обновляются при получении информации о каждом изменении конфигурации. Эти файлы нужны при старте узла, чтобы обнаружить PDisk'ы и другие узлы кластера, а также установить начальные сетевые соединения. Данные в этих файлах могут быть устаревшими, особенно при выключении узла на длительное время.

Базовый порядок работы Distconf

  1. При старте узла Distconf пытается прочитать конфигурацию с локальных PDisk'ов.
  2. Подключается к случайному узлу хранения для проверки актуальности конфигурации.
  3. Если не удаётся подключиться, но есть кворум подключённых, становится лидером.
  4. Лидер пытается инициировать первоначальную настройку кластера, если она разрешена.
  5. Лидер рассылает актуальную конфигурацию всем узлам через Scatter/Gather.
  6. Узлы сохраняют полученную конфигурацию в локальной области PDisk для метаданных и в директориях --config-dir.

Итоговое распределение конфигурации

Место хранения Содержит
TPDiskMetadataRecord на кворуме из PDisk'ов Истинную актуальную конфигурацию
Локальная директория --config-dir Исходный YAML для старта (может быть устаревшим)
Console Актуальная копия (с минимальной задержкой)
DS-controller Подмножество конфигурации, необходимое для распределённого хранилища