Асинхронная репликация

Reindexer поддерживает механизм асинхронной репликации. У каждого экземпляра сервера, задействованного в репликации, может быть роль Leader или Follower. В качестве Leader может выступать сервер Reindexer Standalone, Built-in, Built-in server (подробнее о вариантах использования — в разделе «Установка Reindexer»). Роль Follower может быть у сервера Standalone и Built-in server: Leader-у нужен доступ к RPC-интерфейсам Follower-ов.

Начиная с версии 4.0.0 в Reindexer используется push-репликация. При таком механизме Leader инициирует отправку сведений об изменениях Follower-ам. В версии 3.x.x использовался механизм асинхронной pull-репликации. При переходе с Reindexer 3.x.x на более поздние версии, использующие push-репликацию, ряд настроек необходимо выполнить вручную (подробнее — в разделе Настройки асинхронной репликации при миграции с Reindexer версии 3.x.x на 5.x.x).

Механизмы асинхронной репликации в Reindexer

Для реализации репликации в Reindexer используется 3 различных механизма:

Механизм Когда используется Особенности
Полная синхронизация с использованием снимка неймспейса (force-sync) 1. При начальной синхронизации для копирования полной структуры и данных из неймспейса Leader в Follower или от одного Follower к другому.
2. В случае ошибки с репликацией WAL (например, WAL устарел, или появились несовместимые изменения в структуре индексов)
В этом режиме Leader создает моментальный снэпшот неймспейса (используется стратегия COW) и отправляет все свои индексы и данные Follower
Запись из локального журнала WAL (wal-sync) Когда Leader устанавливает сетевое соединение с Follower для синхронизации данных (например, при потере связи с Follower) Leader запрашивает LSN из WAL Follower-а, делает запрос к своему локальному WAL (каждая запись в нем — это информация про отдельный документ, либо update/delete-запрос), на выходе формирует снэпшот, содержащий только недостающие записи из WAL, и применяет из него все записи. В результате все данные в WAL Leader и Follower совпадают
Онлайн-обновления WAL в реальном времени (онлайн репликация) Когда установлено соединение между Leader и Follower Leader отправляет поток обновлений WAL на всех подключенных Follower-ов. Этот режим требует меньше ресурсов процессора и памяти Leader

Каскадная репликация

Reindexer также поддерживает каскадную репликацию. В этом случае может быть только один Leader. Но у Follower-ов могут быть свои Follower-ы:

             Leader
           /        \
 Follower1           Follower2
     |                /     \
Follower1.1   Follower2.1 Follower2.2

На этой схеме Follower1 и Follower2 реплицируют данные своим Follower-ам, но при этом не предоставляют возможность изменения данных для внешних клиентов.

Настройка асинхронной репликации в Reindexer, начиная с версии 4.0.0

Настройка репликации в Reindexer возможна двумя способами: через служебный неймспейс #config и с помощью файлов конфигурации.

Настройка репликации через служебный неймспейс #config

Репликация настраивается на стороне Leader. Для этого в служебном неймспейсе #config:

  1. Задаются общие параметры репликации. Подробнее — в разделе «Настройка общих параметров репликации».
  2. Устанавливаются специальные параметры для асинхронной репликации.

Настройка специальных параметров асинхронной репликации

Специальные параметры для асинхронной репликации задаются в служебном неймспейсе #config ноды-Leader, в айтеме async_replication:

{
  "type": "async_replication",
  "async_replication": {
    "role": "none",
    "sync_threads": 4,
    "syncs_per_thread": 2,
    "online_updates_timeout_sec": 20,
    "online_updates_delay_msec": 100,
    "sync_timeout_sec": 60,
    "retry_sync_interval_msec": 30000,
    "enable_compression": true,
    "batching_routines_count": 100,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "log_level": "info",
    "max_wal_depth_on_force_sync": 1000,
    "namespaces": [],
    "self_replication_token": "<some_token>",
    "nodes": [
      {
        "dsn": "cproto://192.168.1.5:6534/mydb",
        "namespaces": []
      }
    ]
  }
}

Здесь:

  • role — роль экземпляра сервера в репликации. Возможные значения:
    • none — репликация отключена,
    • follower — сервер участвует в репликации как Follower,
    • leader — сервер участвует в репликации как Leader.
  • sync_threads — количество потоков репликации.
  • syncs_per_thread — Максимальное количество одновременных полных синхронизаций для каждого потока репликации.
  • online_updates_timeout_sec — время ожидания ответа от Follower для режима онлайн-репликации. Задается в секундах.
  • online_updates_delay_msec — задержка реплицирования для онлайн-репликации. Использование этой задержки позволяет значительно сократить потребление CPU за счёт объединения отдельных записей в батчи (по умолчанию используется задержка 100 мс). Напрямую влияет на время, за которое одиночные записи реплицируются на Follower-ов. Задаётся в миллисекундах.
  • sync_timeout_sec — время на запись чанка из снэпшота. Если запись не произошла за это время, то соединение разрывается и происходит полная синхронизация. Задается в секундах.
  • retry_sync_interval_msec — таймаут повторной попытки синхронизации при возникновении сетевых ошибок.
  • enable_compression — флаг для включения/отключения сжатия сетевого трафика.
  • batching_routines_count — число корутин в рамках каждого из потоков. Увеличение числа корутин снизит задержки, связанные с сетью, за счёт большей асинхронности, но увеличит потребление памяти.
  • log_level — уровень логирования репликатора при запуске. Возможные значения: none, error, warning, info, trace.
  • max_wal_depth_on_force_sync — максимальное количество записей WAL, которые будут скопированы после запуска полной синхронизации. Увеличение значения этого параметра может помочь избежать force-syncs синхронизации после переключения Leader, а также увеличивает расход оперативной памяти во время синхронизации.
  • namespaces — список неймспейсов для репликации. Если он пустой, будет выполняться репликация всех неймспейсов из базы данных. Все реплицируемые неймспейсы на Follower-ах будут иметь режим read-only.
  • self_replication_token - опциональный токен, c которым текущая нода подключается к своим follower-ам. Репликация будет возможна, только в случае его совпадения с admissible-токеном из replication.conf follower-а для реплицируемых неймспейсов. Используется как часть механизма проверки, позволяющего избегать репликации на некорректную follower-ноду. Доступно с версии v5.2.0.
  • nodes — список Follower-ов и их настройки:
    • dsn — DSN ноды—Follower-а. Должен задаваться в cproto-формате. Содержит адрес и порт для доступа к Follower по протоколу RPC и имя БД Follower, в которую будут реплицироваться данные.
    • namespaces — необязательный список неймспейсов для репликации на данную ноду-Follower. Если он не задан, будет использоваться значение из общей конфигурации репликации.

Настройка репликации с помощью файлов конфигурации

При этом способе настройки используются YAML-файлы конфигурации:

Их необходимо разместить в директории реплицируемой базы данных.

Файлы replication.conf и async_replication.conf считываются при старте СУБД. Если в этот момент они существуют, то их содержимое будет синхронизироваться в рантайме со служебным неймспейсом #config: то, что записано в файлы, будет перенесено в неймспейс, и наоборот.

Если же файлов нет в директории реплицируемой базы данных при старте сервера, будут применяться настройки репликации из неймспейса #config, а содержимое добавленных replication.conf и async_replication.conf будет игнорироваться до перезапуска. При этом, если файлов нет при старте сервера, создаваться автоматически они не будут.

Содержимое файла конфигурации replication.conf аналогично содержимому поля replication служебного неймспейса config. Пример файла конфигурации для настройки общих параметров репликации.

Содержимое файла конфигурации async_replication.conf аналогично содержимому поля async_replication служебного неймспейса config. Пример файла конфигурации для настройки специальных параметров асинхронной репликации.

Проверка статуса репликации

Метаданные репликации доступны в служебном неймспейсе #memstats. Пример запроса для их получения через reindexer_tool:

Reindexer> SELECT name,replication FROM #memstats WHERE name='media_items'

В ответ вернется JSON-объект со сведениями о статусе репликации для неймспейса (в примере выше проверяется статус репликации для неймспейса media_items):

{
  "items": [
    {
      "name": "media_items",
      "replication": {
        "last_lsn": 3,
        "last_lsn_v2": {
          "server_id": 0,
          "counter": 3
        },
        "temporary": false,
        "incarnation_counter": 0,
        "data_hash": 6806236826,
        "data_count": 3,
        "updated_unix_nano": 1655797373139806200,
        "ns_version": {
          "server_id": 0,
          "counter": 9
        },
        "clusterization_status": {
          "leader_id": 0,
          "role": "simple_replica"
        },
        "wal_count": 3,
        "wal_size": 108
      }
    }
  ],
  "namespaces": [
    "#memstats"
  ],
  "cache_enabled": false
}

Здесь:

  • last_lsn — (deprecated-поле) Значение LSN.
  • last_lsn_v2: - LSN последней операции по изменению данных неймспейса на ноде. При репликации значения LSN для каждой записи Leader и Follower будут иметь одинаковые значения.
    • server_id — id ноды, являющейся источником этого LSN.
    • counter — количество LSN на ноде.
  • temporary — флаг, указывающий, является ли неймспейс временным.
  • incarnation_counter — количество переключений между Leader и Follower.
  • data_hash — хэш данных неймспейса.
  • data_count — количество записей в неймспейсе.
  • updated_unix_nano — время выполнения последнего обновления данных.
  • ns_version:
    • server_id — id ноды, id ноды, являющейся источником этого LSN.
    • counter — текущая версия неймспейса.
  • clusterization_status:
    • leader_id — server_id ноды, которая является лидером для этой реплики,
    • role — текущая роль в репликации. Возможные значения:
      • simple_replica — асинхронная read-only реплика;
      • cluster_replica — один из Follower-ов в синхронном RAFT-кластере;
      • noneLeader в асинхронной репликации, в синхронном кластере, либо неймспейс не участвует в репликации.
  • wal_count — количество записей в WAL.
  • wal_size — размер WAL.

Статистические данные и текущий статус по репликации доступны в служебном неймспейсе #replicationstats. Для их получения выполните запрос:

Reindexer> SELECT * FROM #replicationstats where type='async'
SELECT * FROM #replicationstats where type = 'async'
curl --location 'http://127.0.0.1:9098/api/v1/db/new_db/query' \
--header 'Content-Type: application/json' \
--data '{
  "namespace": "#replicationstats",
  "type": "select",
  "filters": [
    {
      "field": "type",
      "cond": "EQ",
      "value": "async"
    }
  ]
}'

Для полученния информации по асинхронной репликации запрос к неймспейсу #replicationstats для получения статистических сведений по репликации должен содержать условие с фильтрацией по полю type со значением async.

В ответ вернется JSON-объект вида:

{
  "type": "async",
  "force_syncs": {
    "count": 0,
    "max_time_us": 0,
    "avg_time_us": 0
  },
  "wal_syncs": {
    "count": 0,
    "max_time_us": 0,
    "avg_time_us": 0
  },
  "update_drops": 0,
  "pending_updates_count": 275,
  "allocated_updates_count": 275,
  "allocated_updates_size": 43288,
  "nodes": [
    {
      "dsn": "cproto://127.0.0.1:14000/node0",
      "server_id": -1,
      "pending_updates_count": 60,
      "status": "offline",
      "sync_state": "awaiting_resync",
      "role": "none",
      "is_synchronized": false,
      "namespaces": [],
      "last_error":{
        "code": 12,
        "message": "Connection refused"
      }
    },
    {
      "dsn": "cproto://127.0.0.1:14002/node2",
      "server_id": -1,
      "pending_updates_count": 0,
      "status": "online",
      "sync_state": "online_replication",
      "role": "follower",
      "is_synchronized": false,
      "namespaces": [],
      "last_error":{
        "code": 0,
        "message": ""
      }
    },
    {
      "dsn": "cproto://127.0.0.1:14003/node3",
      "server_id": -1,
      "pending_updates_count": 0,
      "status": "online",
      "sync_state": "online_replication",
      "role": "follower",
      "is_synchronized": false,
      "namespaces": [],
      "last_error":{
        "code": 0,
        "message": ""
      }
    },
    {
      "dsn": "cproto://127.0.0.1:14004/node4",
      "server_id": -1,
      "pending_updates_count": 275,
      "status": "online",
      "sync_state": "online_replication",
      "role": "follower",
      "is_synchronized": false,
      "namespaces": [],
      "last_error":{
        "code": 0,
        "message": ""
      }
    }
  ]
}

Здесь:

  • force_syncs — глобальная статистика по полным синхронизациям неймспейсов Follower-ов:
  • wal_syncs — глобальная статистика по WAL-синхронизациям неймспейсов Follower-ов.
  • update_drops — количество переполнений буфера обновлений. Обновления сначала собираются в очередь, а затем отправляются по сети. Если количество обновлений в очереди в какой-то момент превышает ограничение, заданное при запуске сервера, очередь очищается и происходит синхронизация при помощи других механизмов.
  • pending_updates_count — количество обновлений в очереди, ожидающих репликации.
  • allocated_updates_count — количество обновлений в очереди, включая уже реплицированные, но еще не удаленные.
  • allocated_updates_size — общий размер обновлений, указанных в allocated_updates_count, в байтах.
  • nodes — статистика по каждому узлу кластера:
    • dsn — DSN узла,
    • server_id — уникальный идентификатор сервера (ноды), для асинхронной репликации не задан и всегда равен -1,
    • pending_updates_count — количество обновлений в очереди, ожидающих репликации на данного Follower-а,
    • status — статус подключения узла к сети. Возможные значения: none, online, offline,
    • sync_state — статус синхронизации/репликации узла. Возможные значения:
      • none,
      • online_replication,
      • awaiting_resync,
      • syncing.
    • role — роль ноды в репликации. Для асинхронной репликации в этом поле всегда будет follower,
    • is_synchronized — для асинхронной репликации всегда false,
    • namespaces — неймспейсы, для которых настроена репликация на этом узле.
    • last_error — сведения о последней ошибки репликации на эту ноду (если была):
      • code — внутренний код ошибки,
      • message — описание ошибки.

Интерактивное управление репликацией

В Reindexer также поддерживается несколько команд для интерактивного управления репликацией через reindexer_tool или веб-интерфейс.

Перезапуск репликации

При отправке запроса на Leader процесс репликации перезапускается полностью. Пример запроса через reindexer_tool

Reindexer> \upsert #config { "type":"action","action":{ "command":"restart_replication" } }

Сброс роли в репликации

Команда будет полезна, если вы хотите отменить участие Follover-а в репликации и сделать его неймспейсы доступными для изменения, отключив режим read-only. Пример запроса для сброса роли в репликации через reindexer_tool:

Reindexer> \upsert #config { "type":"action","action":{ "command":"reset_replication_role" } }

Сброс роли в репликации для определенного неймспейса

Позволяет отключить на Follower-е репликацию определенного неймспейса и сделать его доступным для изменения (отключить режим read-only). Пример запроса через reindexer_tool:

Reindexer> \upsert #config { "type":"action","action":{ "command":"reset_replication_role", "namespace": "ns_name" } }

Помимо сброса роли в репликации (в том числе и для определенного неймспейса) и отключения режима read-only для ноды, ее нужно вывести из репликации в настройках Leader. Для этого удалите данного Follower-а из поля nodes в неймспейсе #config Leader-а. Если этого не сделать, произойдет force-sync и нода снова станет Follower-ом.

Управление уровнем логирования репликатора

Команда позволяет менять уровень логирования асинхронного и кластерного (синхронного) репликаторов без их перезапуска. Логирование осуществляется через core-логгер, поэтому для info/trace-логов репликатора требуется уровень core-логирования не ниже, чем info.

Reindexer> \upsert #config { "type":"action","action":{ "command":"set_log_level", "type": "async_replication", "level":"trace" } }

Возможные значения types:

  • async_replication (для логов асинхронного репликатора),
  • cluster (для логов синхронного репликатора и логов RAFT).

Возможные значения levels:

  • none,
  • error,
  • warning,
  • info,
  • trace.

Настройки асинхронной репликации при миграции с Reindexer версии 3.x.x на 5.x.x

В версии Reindexer 3.x.x использовался механизм pull-репликация. Ее настройки несовместимы с настройками репликации Reindexer 4.0.0 и более поздних релизов. Поэтому вам придется выполнить конфигурацию вручную.

Для этого требуется ряд изменений в объектах replication и async_replication в служебном неймспейсе #config. Id серверов и кластеров в объекте replication служебного неймспейса #config для Leader-а и Follower-ов изменять при этом необязательно.

Для правильной работы репликации на версих 4.0.0 и выше важно, чтобы каждый из серверов в цепочке асинхронных реплик имел уникальный server_id. При этом в каскадном варианте Follower-ы, находящиеся на одном уровне могут иметь совпадающие server_id, если не предполагается их прямой или опосредованной (через другую Follower-ноду) связи друг с другом.

Репликация в версиях 4.х.х и 5.х.х полностью совместима и не требует каких-либо дополнительных настроект при обновлении.

После обновления с 3.x.x на 4.x.x/5.x.x и включения репликации произойдёт “полная синхронизация (force sync)” между двумя обновлёнными нодами. Это связано с тем, что Reindexer’у 4.x.x/5.x.x требуются дополнительные метаданные асоциированные с документами, которые не реплицировались на версии 3.x.x.

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

Пример миграции для одноуровневой схемы асинхронной репликации

В качестве примера рассмотрим миграцию для следующей схемы репликации:

              leader(192.168.10.1)
             /                  \
   follower1(192.168.10.2)   follower2(192.168.10.3)

Настройки для follower1

Настройки репликации для follower1 в объекте replication:

{
  "type":"replication",
  "replication":{
  	"role":"slave",
  	"master_dsn":"cproto://192.168.10.1/db",
  	"cluster_id":2,
  	"server_id":1,
  	"force_sync_on_logic_error": false,
  	"force_sync_on_wrong_data_hash": false,
  	"namespaces":
  	  - "ns1"
  }
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 1
  }
}

Deprecated параметры должны быть удалены из настроек Follower-а.

Reindexer> \upsert #config { "type": "replication", "replication": { "server_id": 1, "cluster_id": 2 } }

Настройки для follower2

Настройки репликации для follower2 в объекте replication:

{
  "type":"replication",
  "replication":{
  	"role":"slave",
  	"master_dsn":"cproto://192.168.10.3/db",
  	"cluster_id":2,
  	"server_id":2,
  	"force_sync_on_logic_error": false,
  	"force_sync_on_wrong_data_hash": false,
  	"namespaces":
  	  - "ns2"
  	  - "ns3"
  }
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 2
  }
}

Deprecated-параметры должны быть удалены из настроек Follower-а.

Reindexer> \upsert #config { "type": "replication", "replication": { "server_id": 2, "cluster_id": 2 } }

Настройки для всех Follower

В ходе миграции для всех Follower-ов нужно задать роль follower в объекте async_replication служебного неймспейса #config:

{
  "type": "async_replication",
  "async_replication": {
    "role": "follower"
  }
}

Пример запроса на изменение настроек через reindeхer_tool:

Reindexer> \upsert #config { "type": "async_replication", "async_replication": { "role":"follower" } }

Настройки для Leader

Настройки репликации для leader в объекте replication:

{
  "type": "replication",
  "replication": {
    "role": "master",
    "master_dsn": "",
    "cluster_id": 2,
    "server_id": 0
  }
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 0
  }
}
Reindexer> \upsert #config { "type": "replication", "replication": { "server_id": 0, "cluster_id": 2 } }
Настройки репликации для leader в объекте async_replication для Reindexer 4.x.x:

Для leader в ходе миграции нужно создать новую конфигурацию в объекте async_replication служебного неймспейса #config:

{
  "type": "async_replication",
  "async_replication": {
    "role": "leader",
    "sync_threads": 4,
    "syncs_per_thread": 2,
    "online_updates_timeout_sec": 60,
    "retry_sync_interval_msec": 30000,
    "enable_compression": true,
    "batching_routines_count": 100,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "namespaces": [],
    "nodes": [
      {
        "dsn": "cproto://192.168.10.2:6534/mydb",
        "namespaces": [
          "ns1"
        ]
      },
      {
        "dsn": "cproto://192.168.10.3:6534/mydb",
        "namespaces": [
          "ns2",
          "ns3"
        ]
      }
    ]
  }
}

Пример HTTP-запроса для создания новой конфигурации:

curl --location --request PATCH 'http://192.168.10.1/api/v1/db/testdb/namespaces/%23config/items' \
--header 'Content-Type: application/json' \
--data-raw '{
  "type": "async_replication",
  "async_replication": {
    "role": "leader",
    "sync_threads": 4,
    "syncs_per_thread": 2,
    "online_updates_timeout_sec": 60,
    "retry_sync_interval_msec": 30000,
    "enable_compression": true,
    "batching_routines_count": 100,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "namespaces": [],
    "nodes": [
      {
        "dsn": "cproto://192.168.10.2:6534/mydb",
        "namespaces": [
          "ns1"
        ]
      },
      {
        "dsn": "cproto://192.168.10.3:6534/mydb",
        "namespaces": [
          "ns2",
          "ns3"
        ]
      }
    ]
  }
}'

Миграция с Reindexer версии 3.x.x на 4.x.x при использовании каскадной асинхронной репликации

Миграция для каскадной репликации не сильно отличается от миграции для других схем. Как и в случае с одноуровневой схемой репликации, потребуется изменить конфигурацию в объектах replication и async_replication в служебных неймспейсах #config нод.

Ниже представлены примеры конфигурации для нод, участвующих в каскадной репликации до и после миграции, для схемы:

   leader(192.168.10.1)
             |
   follower1(192.168.10.2)
             |
   follower2(192.168.10.3)

Настройки для follower1

Объект replication неймспейса #config для follower1:

{
  "type": "replication",
  "replication": {
    "role": "slave",
    "master_dsn": "cproto://192.168.10.1/db",
    "cluster_id": 2,
    "server_id": 1,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "namespaces": []
  }
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 1
  }
}

Так как follower1 будет выступать в роли Leader-а для follower2 из примера, для него нужно еще задать настройки в объекте async_replication неймспейса #config:

{
  "type": "async_replication",
  "async_replication": {
    "role": "leader",
    "sync_threads": 4,
    "syncs_per_thread": 2,
    "online_updates_timeout_sec": 60,
    "retry_sync_interval_msec": 30000,
    "enable_compression": true,
    "batching_routines_count": 100,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "namespaces": [],
    "nodes": [
      {
        "dsn": "cproto://192.168.10.3:6534/mydb"
      }
    ]
  }
}

Обратите внимание, что в настройках follower1 (который выступает в роли Leader для follower2) в nodes не указывается список неймспейсов для репликации для конкретной ноды-Follower. Список неймспейсов для репликации берется из массива namespaces объекта async_replication.

Настройки для follower2

Объект replication неймспейса #config для follower2:

{
	"type":"replication",
	"replication":{
		"role":"slave",
		"master_dsn":"cproto://192.168.10.2/db",
		"cluster_id":2,
		"server_id":2,
		"force_sync_on_logic_error": false,
		"force_sync_on_wrong_data_hash": false,
		"namespaces": []
	}
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 2
  }
}

Также для follower2 нужно установить соответствующую роль в объекте async_replication неймспейса #config:

{
  "type": "replication",
  "async_replication": {
    "role": "follower"
  }
}

Настройки для leader

Объект replication неймспейса #config для leader:

{
  "type": "replication",
  "replication": {
    "role": "master",
    "master_dsn": "",
    "cluster_id": 2,
    "server_id": 0
  }
}
{
  "type": "replication",
  "replication": {
    "cluster_id": 2,
    "server_id": 0
  }
}

Настройки для leader в объекте async_replication неймспейса #config:

{
  "type": "async_replication",
  "async_replication": {
    "role": "leader",
    "sync_threads": 4,
    "syncs_per_thread": 2,
    "online_updates_timeout_sec": 60,
    "retry_sync_interval_msec": 30000,
    "enable_compression": true,
    "batching_routines_count": 100,
    "force_sync_on_logic_error": false,
    "force_sync_on_wrong_data_hash": false,
    "namespaces": [],
    "nodes": [
      {
        "dsn": "cproto://192.168.10.2:6534/mydb"
      }
    ]
  }
}

Режим совместимости асинхронной репликации с Reindexer 3.x.x

Для Reindexer 5.x.x все предсобранные пакеты деплоятся с включенной опцией совместимости и не требуют ручной пересборки.

Далее будет рассмотрена общая схема постепенно миграции в каскадном кластере с 3.x.x на 5.x.x.

  1. Допустим, исходный кластер выглядит следующим образом (для Follower-ов намеренно используются повторяющиеся значения server_id - они допустимы для несвязанных друг с другом узлов):
                                              leader,v3(server_id: 0)
                                             /                       \
                                           pull                      pull
                                           /                           \
                 follower1,v3(server_id: 1)                             follower2,v3(server_id: 1)
                       /             \                                         /             \
                     pull            pull                                    pull            pull
                      /                \                                      /                \
follower1_1,v3(server_id: 2)   follower1_2,v3(server_id: 2)  follower2_1,v3(server_id: 2)   follower2_2,v3(server_id: 2)   
  1. В этом случае обновление всего кластера следует начинать с leader-ноды:
                                              leader,v5(server_id: 0)
                                             /                       \
                                           pull                      pull
                                           /                           \
                 follower1,v3(server_id: 1)                             follower2,v3(server_id: 1)
                       /             \                                         /             \
                     pull            pull                                    pull            pull
                      /                \                                      /                \
follower1_1,v3(server_id: 2)   follower1_2,v3(server_id: 2)  follower2_1,v3(server_id: 2)   follower2_2,v3(server_id: 2)    

Для узлов follower1 и follower2 такое обновление пройдёт незаметно, потому что они смогут подключиться через pull-репликацию. В async_replication у leader-ноды при этом не должно быть указано никаких Follower-ов (туда прописываются только те, что работают через push-механизм).

  1. Далее можно провести обновление одного из промежуточных Follower-узлов (например, follower1):
                                              leader,v5(server_id: 0)
                                             /                       \
                                           push                      pull
                                           /                           \
                 follower1,v5(server_id: 1)                             follower2,v3(server_id: 1)
                       /             \                                         /             \
                     pull            pull                                    pull            pull
                      /                \                                      /                \
follower1_1,v3(server_id: 2)   follower1_2,v3(server_id: 2)  follower2_1,v3(server_id: 2)   follower2_2,v3(server_id: 2)    

Обновлённый узел потребуется прописать в async_replication у leader-ноды, чтобы она начала на него реплицировать. В этот момент произойдёт force sync между leader и follower1, который распространится по цепочке дальше. То есть через force sync пройдёт вся левая ветка.

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

  1. После этого можно обновить один или несколько оконечных Follower-узлов из левой ветки (на схеме ниже обновлён только follower1_1):
                                              leader,v5(server_id: 0)
                                             /                       \
                                           push                      pull
                                           /                           \
                 follower1,v5(server_id: 1)                             follower2,v3(server_id: 1)
                       /             \                                         /             \
                     push            pull                                    pull            pull
                      /                \                                      /                \
follower1_1,v5(server_id: 2)   follower1_2,v3(server_id: 2)  follower2_1,v3(server_id: 2)   follower2_2,v3(server_id: 2)   

По аналогии с корневым лидером для запуска push-репликации на промежуточном лидере требуется явно указать в async_replication то, что он должен реплицировать на follower1_1.

Обновление follower1_1 здесь приведёт к тому, что произойдёт force sync между follower1 и follower1_1. Если требуется сократить число полных синхронизаций, то имеет смысл на время обновления отключать всю pull-репликацию в обновляемой ветке и включать её после завершения обновления ветки целиком. Тогда на промежуточном и оконечных узлах будет всего по одной force-синхронизации.

  1. Аналогичным образом обновляется следующий оконечный узел (follower1_2):
                                              leader,v5(server_id: 0)
                                             /                       \
                                           push                      pull
                                           /                           \
                 follower1,v5(server_id: 1)                             follower2,v3(server_id: 1)
                       /             \                                         /             \
                     push            push                                    pull            pull
                      /                \                                      /                \
follower1_1,v5(server_id: 2)   follower1_2,v5(server_id: 2)  follower2_1,v3(server_id: 2)   follower2_2,v3(server_id: 2)   

В такой конфигурации follower1_1 и follower1_2 должны быть прописаны в конфигурации async_replication на узле follower1.

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

  1. Аналогичным образом (по частям или целиком) обновляется правая ветка кластера:
                                              leader,v5(server_id: 0)
                                             /                       \
                                           push                      push
                                           /                           \
                 follower1,v5(server_id: 1)                             follower2,v5(server_id: 1)
                       /             \                                         /             \
                     push            push                                    push            push
                      /                \                                      /                \
follower1_1,v5(server_id: 2)   follower1_2,v5(server_id: 2)  follower2_1,v5(server_id: 2)   follower2_2,v5(server_id: 2)