From 349430df41bd1998d3fcbc0c9b4f660a41e5aee6 Mon Sep 17 00:00:00 2001
From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com>
Date: Fri, 5 Aug 2022 04:53:35 +0300
Subject: [PATCH] doc: extend clustering.md

Describe `conf_change_loop` and graceful shutdown logic.
---
 docs/clustering.md | 23 ++++++++++++++++++++++-
 src/traft/node.rs  |  2 ++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/docs/clustering.md b/docs/clustering.md
index 3d2911524a..0e9a547feb 100644
--- a/docs/clustering.md
+++ b/docs/clustering.md
@@ -62,7 +62,7 @@ picodata run --instance-id iN --listen iN --peer i1
 
 # Обработка запросов
 
-### extern "C" fn join()
+### \#\[proc\] fn raft_join()
 
 Значительная часть всей логики по управлению топологией находится в хранимой процедуре `raft_join`. Аргументом для нее является следующая структура:
 
@@ -120,3 +120,24 @@ struct Peer {
 - Генерировать значение `raft_id` может только лидер Raft-группы. Ожидание `ConfChangeV2` лидерства не требует.
 - Помимо всевозможных идентификаторов, ответ содержит список голосующих членов Raft-группы. Они понадобятся новому инстансу чтобы знать адреса соседей и нормально с ними общаться.
 - Также ответ содержит параметр `box_replication`, который требуется для правильной настройки репликации.
+
+# Логика conf_change_loop
+
+Все узлы Raft в кластере делятся на два типа: голосующие (`voter`) и неголосующие (`learner`). На каждом инстансе кластера присутствует поток, управляющий конфигурацией Raft-группы (составом `voters` / `learners`). Реальные изменения тем не менее может генерировать только лидер, на остальных инстансах этот поток спит и ничего не делает.
+
+Поток представляет собой бесконечный цикл. На каждой итерации выполняется проверка, что состав `voters` / `learners` соответствует состоянию инстансов, и при необходимости эти изменения пачкой записываются в Raft-журнал:
+
+- Инстансы в статусе `health: Loading` довавляются как неголосующие.
+- Если есть возможность, `Offline` инстансы передают право голоса другим `Online`.
+- Если общее количество голосующих инстансов оказывается меньше целевого, `Online` инстансы получают право голоса.
+
+Количество голосующих узлов в кластере не настраивается и зависит только от общего количества инстансов. Если инстансов 1 или 2, то голосующий узел один. Если инстансов 3 или 4, то таких узлов три. Для кластера с 5 или более инстансами — пять голосующих узлов.
+
+# Graceful shutdown
+
+Чтобы выключение прошло штатно и не имело негативных последствий необходимо следующее:
+
+- Инстанс не должен оставаться воутером, пока есть другие онлайн кандидаты.
+- Инстанс не должен оставаться лидером.
+
+Чтобы этого добиться, каждый инстанс на `on_shutdown` триггер отправляет лидеру запрос `UpdatePeerRequest{ health: Offline }`. Непосредственно изменением роли `voter` -> `learner` занимается отдеьный поток на лидере (тот самый `conf_change_loop`), инстанс только дожидается его применения.
diff --git a/src/traft/node.rs b/src/traft/node.rs
index acac14e88d..ce99135ec8 100644
--- a/src/traft/node.rs
+++ b/src/traft/node.rs
@@ -317,6 +317,8 @@ impl Node {
         .recv::<Peer>()
     }
 
+    /// Only the conf_change_loop on a leader is eligible to call this function.
+    ///
     /// **This function yields**
     fn propose_conf_change(
         &self,
-- 
GitLab