# Восстановление кластера

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

Репликационный кластер следует рассматривать как одну логическую систему, а не набор независимых серверов. Это обеспечивает многомастерные записи и согласованность данных, но также означает, что вы должны осторожно восстанавливать кворум. В частности, не запускайте команду ручного восстановления, которая восстанавливает записи на сохранившейся стороне, пока не убедитесь, что пропавшие узлы действительно отсутствуют. Эта команда показана далее на этой странице как `SET CLUSTER <name> GLOBAL 'pc.bootstrap' = 1`. Если запустить её слишком рано, можно создать расщепление мозга и получить два независимых кластера.

Для примеров ниже предполагается кластер с узлами A, B и C, если не указано иначе.

## Прежде чем восстанавливать что-либо

Сначала определите, в какой ситуации вы находитесь:

- Есть хотя бы один узел онлайн?
- Узел был остановлен чисто или он упал? Чистая остановка означает, что `searchd` был остановлен нормально и имел время сохранить состояние репликации перед выходом. Падение, потеря питания или `kill -9` не являются чистой остановкой.
- Сохранившаяся часть кластера ещё имеет кворум? Кворум означает, что достаточно узлов могут видеть друг друга, чтобы безопасно оставаться кластером с возможностью записи.
- Если все узлы отключены, какой узел следует запустить первым для восстановления кластера?

Полезные проверки:

- `SHOW STATUS LIKE 'cluster_<name>_status'`
- `SHOW STATUS LIKE 'cluster_<name>_size'`
- `SHOW STATUS LIKE 'cluster_<name>_node_state'`
- если все узлы отключены, проверьте `grastate.dat`, небольшой файл состояния репликации, хранящийся в директории данных кластера. Особенно обратите внимание на `seqno` и `safe_to_bootstrap`: при чистой остановке лучший узел для запуска первым обычно тот, с наиболее продвинутым `seqno` и `safe_to_bootstrap: 1`. Для полной процедуры bootstrap см. [Перезапуск кластера](../../Creating_a_cluster/Setting_up_replication/Restarting_a_cluster.md).

Пример того, как `grastate.dat` может выглядеть после чистой остановки:

```text
# saved replication state
version: 2.1
uuid:    <cluster-uuid>
seqno:   12345
safe_to_bootstrap: 1
```

В этом примере:

- `seqno: 12345` означает, что этот узел знает о транзакциях до порядкового номер 12345
- `safe_to_bootstrap: 1` означает, что этот узел помечен как безопасный для запуска первым

При чистом восстановлении после остановки всех узлов, это обычно тот тип узла, который вы запускаете первым с `--new-cluster` для восстановления кластера.

После восстановления, дождитесь, пока перезапущенный узел сообщает `cluster_<name>_status=primary` и `cluster_<name>_node_state=synced`, прежде чем считать его полностью доступным для записи. Это можно проверить с помощью `SHOW STATUS LIKE 'cluster_<name>_status'` и `SHOW STATUS LIKE 'cluster_<name>_node_state'`. В локальных тестах перезапущенные узлы иногда некоторое время находились в состоянии `cluster_<name>_node_state=joining` и `cluster_<name>_status=disconnected` перед достижением `synced`/`primary`.

### Один узел был остановлен чисто

Если узел A остановлен нормально, узлы B и C продолжают обслуживать записи. Вы можете подтвердить, что кластер всё ещё здоров на этих узлах с помощью `SHOW STATUS LIKE 'cluster_<name>_status'` и `SHOW STATUS LIKE 'cluster_<name>_size'`.

Когда узел A запускается снова, он автоматически присоединяется к кластеру. До завершения синхронизации не отправляйте записи на этот узел. Проверьте `SHOW STATUS LIKE 'cluster_<name>_status'` и `SHOW STATUS LIKE 'cluster_<name>_node_state'` и дождитесь `primary` / `synced`.

Если узлы-доноры B или C ещё имеют все транзакции, которые узел A пропустил, в своём репликационном кэше, узел A может догнать их с помощью инкрементного переноса состояния (IST). IST означает инкрементный перенос состояния. Это означает, что узел получает только транзакции, которые он пропустил, поэтому восстановление обычно быстрее и легче. В противном случае потребуется перенос состояния через снимок (SST). SST означает перенос состояния через снимок. Это означает копирование файлов таблиц из другого узла вместо простого воспроизведения пропущенных транзакций. SST более тяжелый: обычно он медленнее, перемещает больше данных и может сделать восстановление более разрушительным на больших кластерах.

### Два узла были остановлены чисто и один узел остался онлайн

Если узлы A и B остановлены чисто и узел C остаётся онлайн, узел C может продолжать принимать записи. Проверьте `SHOW STATUS LIKE 'cluster_<name>_status'` и `SHOW STATUS LIKE 'cluster_<name>_size'` на узле C, если хотите подтвердить, что он теперь единственный активный узел.

Когда узлы A и B запускаются снова, они автоматически присоединяются и синхронизируются с узлом C. Во время присоединения проверьте `SHOW STATUS LIKE 'cluster_<name>_status'`, `SHOW STATUS LIKE 'cluster_<name>_node_state'`, и `SHOW STATUS LIKE 'cluster_<name>_size'`. Дождитесь, пока все узлы показывают `primary` / `synced` и ожидаемый размер кластера, прежде чем считать восстановление завершенным.

Если узлы A и B были намеренно удалены из кластера, например с помощью `EXIT CLUSTER`, и узел C теперь является единственным сохранённым узлом в кластере, узел C также может восстановиться после собственного чистого перезапуска при обычном запуске демона. В этом случае, когда он один, `--new-cluster` не требуется.

### Все узлы были остановлены чисто

Если все узлы в многонодовом кластере были штатно остановлены, кластер полностью недоступен и его нужно снова запустить особым образом, чтобы один узел мог стать первым запущенным узлом кластера.

При чистой остановке каждый узел записывает свой последний номер транзакции в `grastate.dat`. Узел, который был остановлен последним, является самым безопасным узлом для запуска первым:

- он имеет наиболее продвинутый `seqno`
- он имеет `safe_to_bootstrap: 1`

Запустите этот узел с [`--new-cluster`](../../Creating_a_cluster/Setting_up_replication/Restarting_a_cluster.md). Это указывает Manticore запустить новую копию кластера с этого узла. Если вы запускаете Manticore через systemd в Linux, используйте `manticore_new_cluster`. Он запускает Manticore в режиме `--new-cluster` за вас.

После этого запустите остальные узлы нормально и позвольте им присоединиться. Проверьте восстановление с помощью `SHOW STATUS LIKE 'cluster_<name>_status'`, `SHOW STATUS LIKE 'cluster_<name>_node_state'`, и `SHOW STATUS LIKE 'cluster_<name>_size'`.

Если вы запустите менее продвинутый узел первым, более продвинутый узел позже присоединится к нему и получит полный SST из более старого состояния, что может отбросить транзакции, которые существовали только на более продвинутом узле. Поэтому узел с `safe_to_bootstrap: 1` должен быть вашим первым выбором.

### Воссоздание кластера из существующих локальных таблиц

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

Эта процедура создаёт новый кластер. Она отбрасывает старое членство в кластере и историю репликации, а затем копирует таблицы с одного выбранного узла на другие узлы. Не используйте её, пока на каком-либо узле ещё есть исправная копия старого кластера в состоянии `primary` / `synced`. В этом случае сохраните исправный кластер и восстановите остальные узлы через [JOIN CLUSTER](../../Creating_a_cluster/Setting_up_replication/Joining_a_replication_cluster.md#Joining-a-replication-cluster).

1. Остановите Manticore на каждом узле, который входил в сбойный кластер. Не записывайте данные в локальные таблицы во время этой процедуры.
2. Сделайте резервную копию всего `data_dir` на каждом узле. Сохраните копию старого дескриптора кластера, если в нём есть пользовательские `path`, `nodes` или `options` поставщика, которые вам нужны при воссоздании кластера.
3. Выберите узел, чьи локальные таблицы содержат данные, которые нужно сохранить. Этот узел станет источником для нового кластера. Если позже добавить его таблицы в кластер, таблицы с теми же именами на других узлах будут заменены.
4. На каждом узле отредактируйте `<data_dir>/manticore.json`. В верхнеуровневом объекте `clusters` удалите только запись для сбойного кластера. Например, удалите `clusters.posts` для кластера с именем `posts`.

   Оставьте объект `indexes` и каждый каталог таблицы на месте. Не удаляйте и не создавайте таблицы заново.

5. Запустите Manticore обычным образом на каждом узле. Бывшие таблицы кластера должны теперь отображаться как локальные таблицы. Проверьте таблицы на выбранном исходном узле, прежде чем продолжать. Если они отсутствуют или неполные, остановитесь и восстановите резервную копию.
6. На выбранном исходном узле создайте кластер. Укажите любые сохранённые `path`, `nodes` или параметры поставщика, если они по-прежнему нужны. Если включена аутентификация, добавьте `'<replication-user>' AS user` в этот оператор и в операторы `JOIN CLUSTER` ниже. У учётной записи должны совпадать сохранённые данные аутентификации и право `replication` на каждом узле. См. [настройку репликации](../../Creating_a_cluster/Setting_up_replication/Setting_up_replication.md#Setting-up-replication).

   ```sql
   CREATE CLUSTER posts
   ```

7. На каждом другом узле подключите новый кластер через первый узел:

   ```sql
   JOIN CLUSTER posts AT 'node-a.example:9312'
   ```

8. На выбранном исходном узле присоедините существующие локальные таблицы к новому кластеру:

   ```sql
   ALTER CLUSTER posts ADD table_a, table_b
   ```

9. Дождитесь, пока каждый узел не сообщит `cluster_posts_status=primary` и `cluster_posts_node_state=synced`, прежде чем возобновлять запись.

### Один узел упал или стал недоступным

Если узел A исчезает из-за сбоя или сетевой проблемы, узлы B и C сначала попытаются переподключиться к нему. Если это не удается, они удаляют его из кластера, пересчитывают кворум и продолжают работать как основной кластер.

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

Когда узел A снова запускается, он автоматически присоединяется и наверстывает упущенное так же, как после чистого завершения работы одного узла. Снова используйте `SHOW STATUS LIKE 'cluster_<name>_status'`, `SHOW STATUS LIKE 'cluster_<name>_node_state'` и `SHOW STATUS LIKE 'cluster_<name>_size'`, чтобы подтвердить завершение восстановления.

### Два узла потеряны, и работает только один узел

Если узлы A и B потеряны, и работает только узел C, узел C больше не имеет кворума в трехузловом кластере. Он переключается в состояние `non-primary` и отклоняет запись.

Ошибка записи явная:

```sql
ERROR 1064 (42000): cluster '<name>' is not ready, not primary state (synced)
```

Если узлы A и B только временно отключены, но все еще могут видеть друг друга, они могут продолжать принимать записи, в то время как узел C остается изолированным. Используйте `SHOW STATUS LIKE 'cluster_<name>_status'` на каждой стороне, если вам нужно увидеть, какая сторона все еще доступна для записи.

Если узлы A и B действительно аварийно завершили работу, а узел C — единственная сохранившаяся копия, с которой вы хотите продолжать работать, выполните эту команду на узле C, чтобы снова сделать его доступным для записи:

<!-- example bootstrap-primary -->
Если вы подтвердили, что другие узлы действительно отключены, выполните:

<!-- intro -->
##### SQL:

<!-- request SQL -->

```sql
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
```
<!-- request JSON -->

```json
POST /cli -d "
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
"
```
<!-- end -->

Важно:

- выполняйте это только после того, как убедитесь, что другие узлы недостижимы
- выполняйте это только на стороне, которая должна выжить
- после начальной загрузки узел снова может принимать записи, и другие узлы позже смогут к нему повторно присоединиться

### Все узлы аварийно завершили работу

Если каждый узел аварийно завершил работу, `grastate.dat`, как правило, больше не заслуживает доверия для обычного выбора начальной загрузки. В локальных тестах все узлы показывали:

- `seqno: -1`
- `safe_to_bootstrap: 0`

В этой ситуации выберите узел с самыми свежими данными и запустите его с [`--new-cluster-force`](../../Creating_a_cluster/Setting_up_replication/Restarting_a_cluster.md). Это заставляет Manticore запустить новую копию кластера с этого узла, даже несмотря на то, что обычные метаданные чистого завершения работы не заслуживают доверия. Если вы запускаете Manticore через systemd в Linux, используйте `manticore_new_cluster --force`. Он запускает Manticore в режиме `--new-cluster-force` для вас.

Затем запустите оставшиеся узлы в обычном режиме и позвольте им повторно присоединиться. Проверьте восстановление с помощью `SHOW STATUS LIKE 'cluster_<name>_status'`, `SHOW STATUS LIKE 'cluster_<name>_node_state'` и `SHOW STATUS LIKE 'cluster_<name>_size'`.

### Кластер с четным числом узлов потерял кворум, например, после разделения

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

Если вам необходимо восстановить запись до устранения проблемы с подключением, выберите только одну сторону разделения и выполните ту же команду восстановления там, чтобы эта сторона снова стала доступной для записи. Перед этим проверьте `SHOW STATUS LIKE 'cluster_<name>_status'` на обеих сторонах, чтобы знать, какая сторона в настоящее время является неосновной.

<!-- example bootstrap-primary-split -->
Выберите сторону, которая должна остаться доступным для записи кластером, затем выполните:

<!-- intro -->
##### SQL:

<!-- request SQL -->

```sql
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
```
<!-- request JSON -->

```json
POST /cli -d "
SET CLUSTER posts GLOBAL 'pc.bootstrap' = 1
"
```
<!-- end -->

Никогда не выполняйте этот оператор на обеих сторонах. Если вы это сделаете, вы создадите два отдельных основных кластера, и они не объединятся автоматически при восстановлении сети.

В локальном тестировании внезапная потеря половины четырехузлового кластера воспроизвела то же поведение `non-primary`, и та же команда восстановления вернула одну выжившую половину в состояние `primary`.
<!-- proofread -->
