Изобретая синхронную репликацию

Владислав Шпилевой

План доклада

План доклада

Репликация

История репликации в Tarantool

Алгоритм Raft

Схема асинхронной репликации

Реализация синхронной репликации

Интерфейс

Отличия от Raft

Планы

Репликация [1]

Репликационная группа - "репликасет"

Мастер

Реплика

Реплика

Реплика

Мастер

Мастер

Мастер

Задачи

  • Резервная копия
  • Балансировка запросов

Типы

  • Асинхронная
  • Синхронная

Репликация [2]: асинхронная

Запись в журнал = успешный коммит. Не ждет репликацию

Транзакция

Журнал (мастер)

Коммит и ответ

Репликация

Журнал (реплика)

Транзакция

1

Гибель мастера между (3) и (5) = потеря транзакции.

3

Коммит и ответ

Журнал

2

4

Репликация

Журнал

5

Репликация [3]: потеря данных

{money: 100}

{money: 150}

У меня 100 денег

Кладу еще 50

У меня 150 денег

{money: 100}

{money: 100}

Где мои 150 денег?!

При асинхронной репликации гарантии - почти как без репликации

{money: +50}

{success}

get({money})

{money: 100}

Журнал

Начало репликации...

Репликация [4]: грязные чтения

_ = box.schema.create_space(‘test’)
_ = _:create_index(‘pk’)
box.space.test:replace{1, 100}

Инициализация схемы данных

Выполнение транзакции

wait_replication()

Ручное ожидание репликации

Здесь изменения уже видимы!

Репликация [5]: синхронная

Транзакция

Журнал

(мастер)

Репликация

Журнал

(реплика)

Коммит и ответ

Репликация гарантируется до коммита

1

Транзакция

2

Журнал

Коммит и ответ

5

3

Репликация

Журнал

4

Репликация [6]: кворум

Типичные конфигурации

1 + 1

Тройка

50% + 1

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

Репликация [7]: сравнение

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

Высокая скорость

Хорошая доступность

Легко конфигурировать

Легко потерять данные

Хрупкая доступность

Сложность конфигурации

Высокая надежность

Низкая скорость

Репликация [8]: Raft

Raft - алгоритм синхронной репликации

Гарантия сохранности данных на > 50% узлов;

Чрезвычайная простота

Проверен временем

Выборы лидера

История разработки  [1]

Год 2015

Появился спрос на синхронность

Выбрали Raft, составили великий план

Модуль SWIM - для сборки кластера

Авто-прокси

Ручные выборы - box.ctl.promote()

Оптимизации репликаци

Raft

История разработки [2]

Создание плана

box.ctl.promote()

SWIM

Смена руководства

Raft

2015

2018

2019

2020

Авто-прокси

Оптимизации репликации

SQL

Vinyl

Рафт [1]: общая схема

Это Синхронная репликация и Выборы лидера

Роли:

- лидер

- реплика

- кандидат

Каждый узел изначально реплика

Узлы хранят персистентный терм - логические часы кластера

Терм: 1

Терм: 1

Терм: 1

Терм: 1

Терм: 1

Ждут лидера. Долго нет лидера - начинаются выборы

Запрос голосов

Терм: 2

Голоса: 1

Терм: 2

Голоса: 1

После набора большинства один становится лидером

Терм: 2

Терм: 2

Голоса: 3

Терм: 2

Голоса: 2

Терм: 2

Терм: 2

Лидер принимает все транзакции, реплицирует, и завершает коммит сразу по достижении кворума

Репликация

Кворум

Коммит

Терм: 2

Терм: 2

Подтверждения

Догонка реплик

Транзакция

Ответ

Рафт [2]: синхронная транзакция

Четыре этапа коммита транзакции

Данные в журналы реплик

Коммит в журнал лидера и ответ пользователю

Данные в журнал лидера

Коммит в журналы реплик

Отказ на любом этапе любого узла не приводит к потере данных после коммита, пока живо 50% + 1 узлов

Рафт [3]: синхронный журнал

Журнал: транзакции, термы, голоса, коммиты

Терм 1

Терм 2

Терм 3

Лидер

Реплика

Реплика

Реплика

1

2

1

2

3

4

5

6

4

3

2

1

1

2

3

4

5

6

7

8

9

Новые транзакции

Последний коммит

5

6

7

Асинхронная репликация в Tarantool [1]

TX-поток

IProto-поток

WAL-поток

Relay-потоки

Сеть

Клиентские соединения

Данные от реплик

Управление соединениями;

Ввод/вывод

Журнал;

Запись на диск

Отправка транзакций на реплики

База данных;

Транзакции;

Файберы

Данные на реплики

Асинхронная репликация в Tarantool [2]

ID транзакции: {Replica ID, LSN}

  • Replica ID - ID узла-создателя
  • LSN - монотонный счетчик

VClock - пары {Replica ID, LSN} со всех узлов, снимок состояния кластера

{0, 0, 0}
{0, 0, 0}
{0, 0, 0}
{1, 0, 0}
{1, 0, 0}
{1, 0, 0}
{1, 2, 0}
{1, 2, 0}
{1, 2, 0}
Replica ID = 1
Replica ID = 2
Replica ID = 3

Набор железных требований

Синхронная репликация в Tarantool [1]

Полная совместимость со старыми версиями, пока синхронность не используется

Не меняется формат журнала, только добавляются новые типы записей

Архитектура Тарантула существенно не меняется

Начало синхронной транзакции

$> sync = box.schema.create_space(
    ‘stest’, {is_sync = true}
):create_index(‘pk’)

$> sync:replace{1}

$> box.begin()
$> sync:replace{2}
$> sync:replace{3}
$> box.commit()

Синхронность - свойство спейса

Это синхронные транзакции

Один синхронный спейс - вся транзакция тоже

$> async = box.schema.create_space(
    ‘atest’, {is_sync = false}
):create_index(‘pk’)

$> box.begin()
$> sync:replace{5}
$> async:replace{6}
$> box.commit()

Коммит начинается с записи в журнал мастера

Ожидание кворума [1]: лимб

После журнала транзакция попадает в лимб

Лимб - это очередь синхронных транзакций в TX потоке

Лимб - это связь между TX, WAL, и Relay потоками

5

7

10

LSN

...

Новые транзакции

Закоммиченные транзакции

TX-поток

Кворум

Ожидание кворума [2]: репликация

Реплика пишет в журнал, в лимб, и отвечает последним примененным vclock. Лимб мастера собирает по ним кворум

Транзакции

{0, 0}
{0, 0}

vclock

vclock

Запись в журнал

{3, 0}

Репликация

Запись в журнал

{3, 0}

Ожидание в лимбе

Подтверждение

{3, 0}

Ожидание в лимбе

3

1

2

3

1

2

3

1

2

3

1

2

3

1

2

Сборка кворума

Ожидание кворума [3]: сборка

Лимб использует особый vclock

  • Replica ID - ID реплики
  • LSN - последний LSN лидера подтвержденный этой репликой
{0, 0, 0}
{0, 0, 0}

vclock журнала

vclock лимба

{0, 0, 0}
{0, 0, 0}

Транзакции

{0, 4, 0}
{0, 4, 0}

Репликация

{0, 4, 0}
{0, 4, 0}

Подтверждения

{4, 4, 4}

Лимб видит, что все транзакции с LSN <= 4 собрали кворум 3

Транзакции

{0, 8, 0}

Репликация

{0, 8, 0}
{0, 8, 0}

Подтверждения

{4, 8, 4}
{8, 8, 4}

Лимб видит, что LSN 4 собрал кворум 3, а LSN 8 - кворум 2

Реплика отказала

Replica ID = 1
Replica ID = 3
Replica ID = 2

Конец синхронной транзакции [1]: коммит

{0, 0, 1, 0, 0}

Лимб

При сборке подтверждений лимб проверяет кворум

{1, 0, 1, 0, 0}

Получено подтверждение от реплики ID = 1. Всего 2, а кворум - 3. Ждать еще

{1, 0, 1, 2, 2}

Получено подтверждение от реплик ID = 4 и 5.

  • LSN 1 собрал 4 - коммит
  • LSN 2 собрал 2 - ждать еще

1

2

3

4

5

6

7

8

...

LSN

Коммит

Новые транзакции

Коммит

Commit({LSN = 1})

Коммит пишется в журнал

{5, 5, 4, 6, 7}

Получены новые подтверждения от всех реплик:

  • LSN 4 - 5 подтверждений
  • LSN 5 - 4 подтверждения
  • LSN 6 - 2 подтверждения
  • LSN 7 - 1 подтверждение

Коммит LSN 5

Коммит

Commit({LSN = 5})

Коммит пишется в журнал

Конец синхронной транзакции [2]: откат

Против бесконечного роста очереди есть таймаут

По истечении удаляются все транзакции, в журнал пишется ROLLBACK, пользователи получают ошибку

Но ROLLBACK не значит, что репликация не произошла. То есть при смерти мастера транзакция может получить коммит на новом мастере

Удаляются все, так как могут быть зависимы по данным

Смена мастера в Tarantool

Автоматически

box.cfg{
    election_mode = <mode>,
    election_timeout = <seconds>,
    replication_synchro_quorum = <count>,
}

Автоматика по второй части протокола Raft

Режим - off/candidate/voter

Таймаут выборов в секундах

Кворум на выборы и репликацию

Вручную

box.ctl.clear_synchro_queue()

Новый лидер должен иметь самый большой LSN старого лидера среди остальных узлов

Смотреть по

box.info.replication

На новом лидере надо позвать

Чтобы очистить лимб

API Raft

box.cfg{
    election_mode = <mode>,
    election_timeout = <seconds>,
    replication_synchro_quorum = <count>,
    replication_synchro_timeout = <seconds>,
    replication_timeout = <seconds>,
}
box.info.replication
box.ctl.clear_synchro_queue()
box.info.election

Опции выборов

Опции синхронной репликации

box.space.<name>:alter{is_sync = true}
box.schema.create_space(name {
    {is_sync = true}
})

Включить на существующем спейсе

Создать новый спейс

Отличия от Raft

Формат журнала

В Raft журнал линейный - один LSN для всех

В Tarantool журнал векторный - у всех свой LSN

Тип журнала

В Raft журнал UNDO - можно откатывать с конца

В Tarantool журнал REDO - нельзя откатить, если уже записано

Открывает дорогу к синхронной мастер-мастер репликации

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

Планы

Мастер-мастер синхронная репликация

Авто-сборка кластера со SWIM

Интеграция с VShard

Ссылки

Пример выборов лидера

Пример синхронной репликации

Официальный сайт