From ff62d393070c24fd37d169956c8f3ce1f223d199 Mon Sep 17 00:00:00 2001
From: Yaroslav Dynnikov <yaroslav.dynnikov@gmail.com>
Date: Wed, 15 Nov 2023 14:55:48 +0300
Subject: [PATCH] instance_lifecycle.md: major edits

---
 docs/architecture/instance_lifecycle.md | 189 ++++++++++++------------
 1 file changed, 97 insertions(+), 92 deletions(-)

diff --git a/docs/architecture/instance_lifecycle.md b/docs/architecture/instance_lifecycle.md
index 0a4cb35b..56044882 100644
--- a/docs/architecture/instance_lifecycle.md
+++ b/docs/architecture/instance_lifecycle.md
@@ -1,39 +1,33 @@
 #  Жизненный цикл инстанса
 
 В контексте операционных систем каждый инстанс соответствует группе из
-двух процессов — родительского (supervisor) и дочернего (именно он
-выполняет tarantool runtime).
+двух процессов. Данный раздел высокоуровнево описывает основные этапы
+инициализации инстанса. Они изображены на следующей схеме.
+
+![main.rs](../images/clustering.svg "main.rs control flow")
 
 Красным показан родительский процесс, который запущен на всем протяжении
-жизненного цикла инстанса. Вся логика, начиная с присоединения к
-кластеру, и заканчивая обслуживанием клиентских запросов, происходит в
+жизненного цикла инстанса. Вся логика, начиная с присоединения (joining)
+к кластеру, и заканчивая обслуживанием клиентских запросов, происходит в
 дочернем процессе (голубой цвет). Единственное предназначение
 родительского процесса — иметь возможность сбросить состояние дочернего
-(выполнить rebootstrap) и инициализировать его повторно (сиреневый
-цвет).
+и инициализировать его повторно (сиреневый цвет).
 
-Данная схема наиболее полно отражает логику кода в файле `main.rs`. Ниже
-описаны детали выполнения каждого этапа и соответствующей программной
-функции.
+Ниже описаны детали выполнения каждого этапа и соответствующей
+программной функции.
 
-### fn main()
+### fn main() {: #fn_main }
 
-На этом этапе происходит ветвление (форк) процесса `picodata`.
-Родительский процесс (supervisor) ожидает от дочернего процесса
-сообщения по механизму IPC и при необходимости перезапускает дочерний
-процесс.
+На этом этапе происходит ветвление (форк) процесса. Родительский
+процесс, именуемый в дальнейшем "supervisor", ожидает от дочернего
+процесса сообщения по механизму IPC и при необходимости перезапускает
+его.
 
 Выполнение дочернего процесса начинается с вызова функции
-[`start_discover()`](#fn-start_discover) и далее следует алгоритму. При
+[`start_discover()`](#fn_start_discover) и далее следует алгоритму. При
 необходимости дочерний процесс может попросить родителя удалить все
-файлы БД (см. раздел [Ребутстрап](#rebootstrap)). Это используется для
-повторной инициализации инстанса с нормальным `replicaset_uuid` вместо
-рандомного.
-
-### Ребутстрап {: #rebootstrap }
-
-В СУБД Tarantool имеются две особенности, из-за которых процесс инициализации
-выглядит следующим образом:
+файлы БД. Это используется для того чтобы корректно учесть две
+особенности Tarantool:
 
 1. Принадлежность инстанса тому или иному репликасету определяется в
    момент первого вызова `box.cfg()`, когда создается первый снапшот.
@@ -47,67 +41,73 @@
   репликасету;
 - принадлежность репликасету невозможно узнать без общения по сети.
 
-Чтобы эту проблему решить, Picodata инициализируется со случайно
-сгенерированными идентификаторами, а позже перезапускает процесс,
-попутно очищая рабочую директорию.
+Чтобы эту проблему решить, инициализация на этапе `start_discover()`
+происходит со случайно сгенерированными идентификаторами, после чего
+supervisor очищает рабочую директорию и перезапускает дочерний процесс с
+функций `start_boot()` или `start_join()`.
 
-### fn start_discover()
+### fn start_discover() {: #fn_start_discover }
 
 Дочерний процесс начинает свое существование с функции
-[`init_common()`](#fn-init_common), в рамках которой в т.ч.
-инициализируется модуль `box`. Возможно, что при этом из БД будет ясно,
-что bootstrap данного инстанса уже был произведен ранее и что raft уже
-знает о вхождении этого инстанса в кластер — в таком случае никакого
-discovery не будет, инстанс сразу перейдет к этапу `postjoin()`. В
-противном случае, если место инстанса в кластере еще не известно,
-алгоритм discovery определяет значение флага `i_am_bootstrap_leader` и
-адрес лидера raft-группы. Далее инстанс сбрасывает свое состояние (см.
-[Ребутстрап](#rebootstrap)), чтобы повторно провести инициализацию
-`box.cfg()`, теперь уже с известными параметрами. Сам лидер
-(единственный с `i_am_bootstrap_leader == true`) выполняет функцию
-`start_boot()`. Остальные инстансы переходят к функции `start_join()`.
-
-### fn start_boot()
-
-В функции `start_boot` происходит инициализация raft-группы — лидер
-генерирует и сохраняет в БД первые записи в журнале. Эти записи
-описывают добавление первого инстанса в пустую raft-группу и создание
-начальной clusterwide-конфигурации. Таким образом достигается
-однообразие кода, обрабатывающего эти записи.
+[`init_common()`](#fn_init_common), в рамках которой в т.ч.
+инициализируется движок базы данных `box.cfg()`.
 
-Сам raft-узел на данном этапе еще не создается. Это произойдет позже, на
-стадии `postjoin()`.
+Возможно, что при этом из БД станет ясно, что данный инстанса уже был
+добавлен в кластер — в таком случае этап discovery пропускается и
+инстанс сразу переходит к этапу [`postjoin()`](#fn_postjoin).
 
-### fn start_join()
+В противном случае, если место инстанса в кластере еще не известно,
+[алгоритм discovery](../architecture/discovery.md) позволяет найти адрес
+лидера или же определить, что им должен стать данный инстанс
+(`i_am_bootstrap_leader`).
 
-Вызову функции `start_join()` всегда предшествует
-[ребутстрап](#rebootstrap) (удаление всех данных и перезапуск процесса),
-поэтому на данном этапе в БД нет ни модуля `box`, ни пространства
-хранения. Функция `start_join()` имеет простое устройство:
+В контексте инициализации кластера важно лишь то, что этот алгоритм
+позволяет выполнить инициализацию не более чем одному инстансу. Если
+таких инстансов было бы несколько, то и кластеров Picodata получилось бы
+несколько.
 
-Инстанс отправляет запрос `rpc::join` лидеру raft-группы (он
-известен после discovery), который в ответе присылает всю необходимую
-для инициализации информацию:
+После discovery при отсутствии ошибок инстанс выполняет процедуру
+"rebootstrap" — сбрасывает свое состояние, чтобы повторно провести
+инициализацию `box.cfg()`, теперь уже с известными параметрами.
+Bootstrap-лидер выполняет [`start_boot()`](#fn_start_boot).
+Остальные инстансы переходят к [`start_join()`](#fn_start_join).
 
-Для инициализации raft-узла:
+### fn start_boot() {: #fn_start_boot }
 
-- идентификатор `raft_id`;
-- данные таблицы `_picodata_peer_address`.
+В функции `start_boot()` происходит инициализация системных глобальных
+таблиц Picodata — лидер генерирует и сохраняет в БД первые записи в
+raft-журнале. Эти записи описывают добавление первого инстанса в пустую
+raft-группу и создание начальной конфигурации кластера.
+
+Сам raft-узел на данном этапе еще не создается. Это произойдет позже, на
+стадии [`postjoin()`](#fn_postjoin).
 
-Для первичного вызова `box.cfg()`:
-- идентификаторы  `instance_uuid`, `replicaset_uuid`,
+### fn start_join() {: #fn_start_join }
+
+Вызову функции `start_join()` всегда предшествует rebootstrap (удаление
+всех данных и перезапуск процесса), поэтому на данном этапе в БД нет ни
+модуля `box`, ни пространства хранения. Функция `start_join()` имеет
+простое устройство:
+
+Инстанс отправляет join-запрос лидеру raft-группы (он известен после
+discovery), который в ответе присылает всю необходимую для инициализации
+информацию:
+
+- идентификатор `raft_id`;
+- данные системной таблицы
+  [`_pico_peer_address`](../architecture/system_tables.md#_pico_peer_address);
+- идентификаторы  `instance_uuid`, `replicaset_uuid`;
 - `box.cfg.replication` — список [адресов](../overview/glossary.md#address) для репликации.
 
 Получив все настройки, инстанс использует их в `box.cfg()` (см.
-[`init_common()`](#fn-init_common)), и затем создает в БД группу
-`_picodata_peer_address` с актуальными адресами других инстансов. Без
-этого инстанс не сможет отвечать на  сообщения от других членов
-raft-группы.
+[`init_common()`](#fn_init_common)), и затем заполняет таблицу
+`_pico_peer_address` актуальными адресами других инстансов. Без этого
+инстанс не сможет отвечать на сообщения от других членов raft-группы.
 
 По завершении этих манипуляций инстанс также переходит к этапу
 `postjoin()`.
 
-### fn postjoin()
+### fn postjoin() {: #fn_postjoin }
 
 Логика функции `postjoin()` одинакова для всех инстансов. К этому
 моменту для инстанса уже инициализированы корректные пространства
@@ -115,47 +115,52 @@ raft-группы.
 
 Функция `postjoin()` выполняет следующие действия:
 
-- инициализирует HTTP-сервер в соответствии с параметром `--http-listen`.
+- инициализирует HTTP-сервер в соответствии с параметром `--http-listen`;
 - запускает Lua-скрипт, указанный в аргументе `--script`;
 - инициализирует узел Raft, который начинает взаимодействовать с
-  raft-группой;
-- в случае, если других кандидатов нет, инстанс тут же
-  избирает себя лидером группы;
-- устанавливает триггер `on_shutdown`, который обеспечит
-  [корректное завершение работы инстанса](#graceful-shutdown).
+  другими инстансами кластера;
+- в случае, если инстанс в кластере всего один, он тут же избирает себя
+  лидером группы;
+- устанавливает триггер `on_shutdown`, который обеспечит корректное
+  завершение работы инстанса.
 
 Последним шагом инстанс оповещает кластер о том, что он готов проходить
 настройку необходимых подсистем (репликации, шардинга, и т.д.). Для
 этого лидеру отправляется запрос на обновление `target_grade` текущего
 инстанса до уровня `Online`, после чего за дальнейшие действия будет
-отвечать специальный поток управления [topology governor](#topology-governor).
+отвечать специальный поток управления [topology
+governor](../overview/glossary.md#governor).
 
-Как только запись с обновленным грейдом будет зафиксирована в Raft, узел
-готов к использованию.
+Как только запись с обновленным `target_grade` будет применена, функция
+`postjoin` завершается. Сам процесс при этом остается запущен и
+продолжает исполнять файберы и обслуживать сетевые запросы.
 
-### fn init_common()
+### fn init_common() {: #fn_init_common }
 
 Функция `init_common` обобщает действия, необходимые для инициализации
-инстанса во всех трех вышеописанных сценариях — `start_discover`,
-`start_boot`, `start_join`.
+инстанса во всех трех вышеописанных сценариях —
+[`start_discover()`](#fn_start_discover),
+[`start_boot()`](#fn_start_boot), [`start_join()`](#fn_start_join).
 
 Инициализация инстанса подразумевает следующие шаги:
 
-- создание `data_dir`;
+- создание директории с данными инстанса (из аргумента `picodata run
+  --data-dir`);
 - первичный вызов `box.cfg`;
-- инициализация `package.preload.vshard`;
+- инициализация журнала событий безопасности;
+- инициализация Lua модулей `vshard` и `http`;
+- инициализация распределенного SQL;
 - инициализация хранимых процедур (`box.schema.func.create`);
-- создание системных таблиц (`_picodata_raft_log` и т.д).
+- создание локальных спейсов `_raft_log` и `_raft_state`;
+- создание [системных таблиц](../architecture/system_tables.md).
 
 Параметры первичного вызова `box.cfg` зависят от конкретного сценария:
 
-| param       | `start_discover` | `start_boot` | `start_join`                  |
-|-------------|------------------|--------------|-------------------------------|
-| listen      | None             | None         | _from args_                   |
-| read_only   | false            | false        | from `rpc::join` response     |
-| uuids       | _random_         | _given_      | from `rpc::join` response     |
-| replication | None             | None         | from `rpc::join` response     |
-| data_dir    | _from args_      | ...          | ...                           |
-| log_level   | _from args_      | ...          | ...                           |
-
-
+| param       | `start_discover` | `start_boot` | `start_join`         |
+|-------------|------------------|--------------|----------------------|
+| listen      | None             | None         | _from args_          |
+| read_only   | false            | false        | from `join` response |
+| uuids       | _random_         | _given_      | from `join` response |
+| replication | None             | None         | from `join` response |
+| data_dir    | _from args_      | _from args_  | _from args_          |
+| log_level   | _from args_      | _from args_  | _from args_          |
-- 
GitLab