Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
D
docs
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
core
docs
Commits
ff62d393
Commit
ff62d393
authored
1 year ago
by
Yaroslav Dynnikov
Browse files
Options
Downloads
Patches
Plain Diff
instance_lifecycle.md: major edits
parent
c45c7502
No related branches found
Branches containing commit
No related tags found
Tags containing commit
1 merge request
!199
Replace clustering.md with instance_lifecycle.md
Pipeline
#28088
passed
1 year ago
Stage: build-base-image
Stage: pack-doc
Stage: upload
Stage: deploy
Changes
1
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
docs/architecture/instance_lifecycle.md
+97
-92
97 additions, 92 deletions
docs/architecture/instance_lifecycle.md
with
97 additions
and
92 deletions
docs/architecture/instance_lifecycle.md
+
97
−
92
View file @
ff62d393
# Жизненный цикл инстанса
В контексте операционных систем каждый инстанс соответствует группе из
двух процессов — родительского (supervisor) и дочернего (именно он
выполняет tarantool runtime).
двух процессов. Данный раздел высокоуровнево описывает основные этапы
инициализации инстанса. Они изображены на следующей схеме.

Красным показан родительский процесс, который запущен на всем протяжении
жизненного цикла инстанса. Вся логика, начиная с присоединения
к
кластеру, и заканчивая обслуживанием клиентских запросов, происходит в
жизненного цикла инстанса. Вся логика, начиная с присоединения
(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_ |
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment