Внутреннее устройство | Tarantool
Документация на русском языке
поддерживается сообществом
Справочники Внутреннее устройство

Внутреннее устройство

Бинарный протокол Tarantool’а представляет собой бинарный запросно-ответный протокол.

0    X
+----+
|    | - X + 1 байт
+----+
 TYPE - тип MsgPack-значения (если это MsgPack-объект)

+====+
|    | - MsgPack-объект изменяемого размера
+====+
 TYPE - тип MsgPack-значения

+~~~~+
|    | - Массив или ассоциативный массив в формате MsgPack изменяемого размера
+~~~~+
 TYPE - тип MsgPack-значения

Типы MsgPack-данных:

  • MP_INT - целое число
  • MP_MAP - ассоциативный массив
  • MP_ARR - массив
  • MP_STRING - строка
  • MP_FIXSTR - строка фиксированной длины
  • MP_OBJECT - любой MsgPack-объект
  • MP_BIN - бинарный формат MsgPack

ПРИВЕТСТВИЕ TARANTOOL'А:

 0                                     63
 +--------------------------------------+
 |                                      |
 | Приветствие Tarantool'а (версия сервера)  |
 |               64 байта               |
 +---------------------+----------------+
 |                     |                |
 | СОЛЬ в кодировке BASE64 |      NULL      |
 |      44 байта       |                |
 +---------------------+----------------+
 64                  107              127

Экземпляр сервера начинает диалог с отправки клиенту текста приветствия фиксированного размера (128 байтов). Приветствие всегда содержит две 64-байтные строки текста в формате ASCII, каждая строка заканчивается символом разрыва строки (\n). Первая строка описывает версию экземпляра и тип протокола. Вторая строка содержит случайную строку в кодировке base64 размером до 44 байтов для использования в пакете аутентификации и заканчивается на пробелы (до 23).

После того, как приветствие прочитано, протокол становится простым запросно-ответным протоколом и предоставляет полный доступ к функциям Tarantool’а, включая:

  • мультиплексирование запросов, т.е. возможность асинхронной отправки множества запросов по одному соединению;
  • формат ответа, который поддерживает запись в режиме без копирования (zero-copy).

Для структуризации и кодирования данных протокол использует формат данных msgpack.

Протокол использует ассоциативные массивы, которые содержат несколько целочисленных постоянных, в качестве ключей. Эти постоянные указаны по ссылке src/box/iproto_constants.h. Ниже приведены часто используемые постоянные:

-- пользовательские ключи
<iproto_sync>          ::= 0x01
<iproto_schema_id>     ::= 0x05  /* также schema_version */
<iproto_space_id>      ::= 0x10
<iproto_index_id>      ::= 0x11
<iproto_limit>         ::= 0x12
<iproto_offset>        ::= 0x13
<iproto_iterator>      ::= 0x14
<iproto_key>           ::= 0x20
<iproto_tuple>         ::= 0x21
<iproto_function_name> ::= 0x22
<iproto_username>      ::= 0x23
<iproto_expr>          ::= 0x27 /* также expression */
<iproto_ops>           ::= 0x28
<iproto_data>          ::= 0x30
<iproto_error>         ::= 0x31
-- -- Значение ключа <code> в запросе может быть следующим:
-- Ключи для команд пользователя
<iproto_select>       ::= 0x01
<iproto_insert>       ::= 0x02
<iproto_replace>      ::= 0x03
<iproto_update>       ::= 0x04
<iproto_delete>       ::= 0x05
<iproto_call_16>      ::= 0x06 /* as used in version 1.6 */
<iproto_auth>         ::= 0x07
<iproto_eval>         ::= 0x08
<iproto_upsert>       ::= 0x09
<iproto_call>         ::= 0x0a
-- Коды для команд администратора
-- (включая коды для инициализации набора реплик и выбора мастера)
<iproto_ping>         ::= 0x40
<iproto_join>         ::= 0x41 /* i.e. replication join */
<iproto_subscribe>    ::= 0x42
<iproto_request_vote> ::= 0x43

-- -- Значение для ключа <code> в ответе может быть следующим:
<iproto_ok>           ::= 0x00
<iproto_type_error>   ::= 0x8XXX /* где XXX -- это значение в errcode.h */

И заголовок <header> и тело сообщения <body> представляют собой ассоциативные массивы в формате msgpack:

Запрос / ответ:

0        5
+--------+ +============+ +===================================+
| BODY + | |            | |                                   |
| HEADER | |   HEADER   | |               BODY                |
|  SIZE  | |            | |                                   |
+--------+ +============+ +===================================+
  MP_INT       MP_MAP                     MP_MAP
УНИФИЦИРОВАННЫЙ ЗАГОЛОВОК:

+================+================+=====================+
|                |                |                     |
|   0x00: CODE   |   0x01: SYNC   |    0x05: SCHEMA_ID  |
| MP_INT: MP_INT | MP_INT: MP_INT |  MP_INT: MP_INT     |
|                |                |                     |
+================+================+=====================+
                          MP_MAP

Они различаются лишь набором допустимых ключей и значений. Ключ определяет тип следующего за ним значения. В запросе может отсутствовать ассоциативный массив в теле сообщения. В ответе же он всегда будет присутствовать (даже при запросе проверки связи PING). schema_id может отсутствовать в заголовке запроса, что означает отсутствие проверки версии, но этот ключ обязательно должен присутствовать в ответе. Если schema_id отправляется в заголовке, будет выполнена соответствующая проверка.

Когда клиент подключается к экземпляру сервера, экземпляр отвечает 128-байтным текстовым сообщением приветствия. Часть приветствия представляет собой закодированное в формате base-64 значение соль для сессии (случайная строка), которое можно использовать для аутентификации. Длина расшифрованного значения соль (44 байта) выходит за пределы сообщения для аутентификации (первые 20 байтов). Остаток предназначается для будущих схем аутентификации.

ПОДГОТОВКА КОДИРОВАНИЯ:

    LEN(ENCODED_SALT) = 44;
    LEN(SCRAMBLE)     = 20;

подготовить кодирование 'chap-sha1':

    salt = base64_decode(encoded_salt);
    step_1 = sha1(password);
    step_2 = sha1(step_1);
    step_3 = sha1(salt, step_2);
    scramble = xor(step_1, step_3);
    return scramble;

ТЕЛО СООБЩЕНИЯ АВТОРИЗАЦИИ: CODE = 0x07

+==================+====================================+
|                  |        +-------------+-----------+ |
|  (KEY)           | (TUPLE)|  len == 9   | len == 20 | |
|   0x23:USERNAME  |   0x21:| "chap-sha1" |  SCRAMBLE | |
| MP_INT:MP_STRING | MP_INT:|  MP_STRING  |  MP_BIN   | |
|                  |        +-------------+-----------+ |
|                  |                   MP_ARRAY         |
+==================+====================================+
                        MP_MAP

<key> содержит имя пользователя. <tuple> должен представлять собой массив из 2 полей: механизм аутентификации (в данный момент поддерживается только механизм «chap-sha1») и пароль, закодированный в соответствии с указанным механизмом. Аутентификация в Tarantool’е необязательна: если аутентификация не проводится, то пользователем в сессии будет „guest“. Экземпляр отвечает на пакет аутентификации стандартным ответом с 0 кортежей.

  • SELECT: CODE - 0x01 Поиск кортежей, соответствующих шаблону поиска
ТЕЛО СООБЩЕНИЯ ВЫБОРКИ SELECT:

+==================+==================+==================+
|                  |                  |                  |
|   0x10: SPACE_ID |   0x11: INDEX_ID |   0x12: LIMIT    |
| MP_INT: MP_INT   | MP_INT: MP_INT   | MP_INT: MP_INT   |
|                  |                  |                  |
+==================+==================+==================+
|                  |                  |                  |
|   0x13: OFFSET   |   0x14: ITERATOR |   0x20: KEY      |
| MP_INT: MP_INT   | MP_INT: MP_INT   | MP_INT: MP_ARRAY |
|                  |                  |                  |
+==================+==================+==================+
                          MP_MAP
  • INSERT: CODE - 0x02 Вставка кортежа в спейс, если нет кортежей с такими же уникальными ключами. Если есть, выдать ошибку duplicate key (повторяющееся значение ключа).
  • REPLACE: CODE - 0x03 Вставка кортежа в спейс или замена существующего кортежа.
ТЕЛО СООБЩЕНИЯ ВСТАВКИ/ЗАМЕНЫ INSERT/REPLACE:

+==================+==================+
|                  |                  |
|   0x10: SPACE_ID |   0x21: TUPLE    |
| MP_INT: MP_INT   | MP_INT: MP_ARRAY |
|                  |                  |
+==================+==================+
                 MP_MAP
  • UPDATE: CODE - 0x04 Обновление кортежа
ТЕЛО СООБЩЕНИЯ ОБНОВЛЕНИЯ UPDATE:

+==================+=======================+
|                  |                       |
|   0x10: SPACE_ID |   0x11: INDEX_ID      |
| MP_INT: MP_INT   | MP_INT: MP_INT        |
|                  |                       |
+==================+=======================+
|                  |          +~~~~~~~~~~+ |
|                  |          |          | |
|                  | (TUPLE)  |    OP    | |
|   0x20: KEY      |    0x21: |          | |
| MP_INT: MP_ARRAY |  MP_INT: +~~~~~~~~~~+ |
|                  |            MP_ARRAY   |
+==================+=======================+
                 MP_MAP
OP:
    Работает только для целочисленных полей:
    * Сложение    OP = '+' . space[key][field_no] += argument
    * Вычитание OP = '-' . space[key][field_no] -= argument
    * Побитовое И OP = '&' . space[key][field_no] &= argument
    * Исключающее ИЛИ OP = '^' . space[key][field_no] ^= argument
    * Побитовое ИЛИ  OP = '|' . space[key][field_no] |= аргумент
    Работает для любых полей:
    * Удаление      OP = '#'
      удалить поля <argument>, начиная
      с поля <field_no> в спейсе с ключом space[<key>]

0           2
+-----------+==========+==========+
|           |          |          |
|    OP     | FIELD_NO | ARGUMENT |
| MP_FIXSTR |  MP_INT  |  MP_INT  |
|           |          |          |
+-----------+==========+==========+
              MP_ARRAY
    * Вставка      OP = '!'
      вставить <argument> до поля <field_no>
    * Присвоение      OP = '='
      присвоить <argument> полю <field_no>.
      увеличит кортеж, если <field_no> == <max_field_no> + 1

0           2
+-----------+==========+===========+
|           |          |           |
|    OP     | FIELD_NO | ARGUMENT  |
| MP_FIXSTR |  MP_INT  | MP_OBJECT |
|           |          |           |
+-----------+==========+===========+
              MP_ARRAY

    Работает со строковыми полями:
    * Разделение      OP = ':'
      взять строку из space[key][field_no] и
      заменить <offset> байтов из положения <position> на <argument>
0           2
+-----------+==========+==========+========+==========+
|           |          |          |        |          |
|    ':'    | FIELD_NO | POSITION | OFFSET | ARGUMENT |
| MP_FIXSTR |  MP_INT  |  MP_INT  | MP_INT |  MP_STR  |
|           |          |          |        |          |
+-----------+==========+==========+========+==========+
                         MP_ARRAY

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

  • DELETE: CODE - 0x05 Удаление кортежа
ТЕЛО СООБЩЕНИЯ УДАЛЕНИЯ DELETE:

+==================+==================+==================+
|                  |                  |                  |
|   0x10: SPACE_ID |   0x11: INDEX_ID |   0x20: KEY      |
| MP_INT: MP_INT   | MP_INT: MP_INT   | MP_INT: MP_ARRAY |
|                  |                  |                  |
+==================+==================+==================+
                          MP_MAP
  • CALL_16: CODE - 0x06 Вызов хранимой функции с возвратом массива кортежей. Объявлен устаревшим; рекомендуется использовать CALL (0x0a).
ТЕЛО СООБЩЕНИЯ CALL_16:

+=======================+==================+
|                       |                  |
|   0x22: FUNCTION_NAME |   0x21: TUPLE    |
| MP_INT: MP_STRING     | MP_INT: MP_ARRAY |
|                       |                  |
+=======================+==================+
                    MP_MAP
  • EVAL: CODE - 0x08 Оценка Lua-выражения
ТЕЛО СООБЩЕНИЯ EVAL:

+=======================+==================+
|                       |                  |
|   0x27: EXPRESSION    |   0x21: TUPLE    |
| MP_INT: MP_STRING     | MP_INT: MP_ARRAY |
|                       |                  |
+=======================+==================+
                    MP_MAP
  • UPSERT: CODE - 0x09 Обновление кортежа, если он уже существует, попытка вставить кортеж. Всегда используйте первичный индекс.
ТЕЛО СООБЩЕНИЯ ОБНОВЛЕНИЯ И ВСТАВКИ UPSERT:

+==================+==================+==========================+
|                  |                  |             +~~~~~~~~~~+ |
|                  |                  |             |          | |
|   0x10: SPACE_ID |   0x21: TUPLE    |       (OPS) |    OP    | |
| MP_INT: MP_INT   | MP_INT: MP_ARRAY |       0x28: |          | |
|                  |                  |     MP_INT: +~~~~~~~~~~+ |
|                  |                  |               MP_ARRAY   |
+==================+==================+==========================+
                                MP_MAP

Структура операции аналогична структуре операции обновления UPDATE.
   0           2
+-----------+==========+==========+
|           |          |          |
|    OP     | FIELD_NO | ARGUMENT |
| MP_FIXSTR |  MP_INT  |  MP_INT  |
|           |          |          |
+-----------+==========+==========+
              MP_ARRAY

Поддерживаются следующие операции:

'+' - прибавление значения к числовому полю. Если поле не является числовым, оно
      сначала изменяется на 0. Если поле отсутствует, операция
      пропускается. В случае переполнения ошибки также не будет, значение
      просто переносится в стиле языка C. Диапазон целых чисел в формате MsgPack:
      от -2^63 до 2^64-1
'-' - как в предыдущей операции, но значение вычитается
'=' - присвоение значения полю. Если поле отсутствует,
      операция пропускается.
'!' - вставка поля. Можно вставить поле, если при этом не будут созданы
      промежутки с нулевым значением nil между полями. Например, можно добавить поле между
      существующими полями или последнее поле в кортеже.
'#' - удаление поля. Если поле отсутствует, операция пропускается.
      Нельзя с помощью операции обновления update изменить компонент первичного
      ключа (это проверяется перед выполнением операции upsert).
  • CALL: CODE - 0x0a Аналог CALL_16, но как и операция EVAL, CALL возвращает список неконвертированных значений
ТЕЛО СООБЩЕНИЯ CALL:

+=======================+==================+
|                       |                  |
|   0x22: FUNCTION_NAME |   0x21: TUPLE    |
| MP_INT: MP_STRING     | MP_INT: MP_ARRAY |
|                       |                  |
+=======================+==================+
                    MP_MAP

Здесь мы продемонстрируем пакеты полностью:

OK:    LEN + HEADER + BODY

0      5                                          OPTIONAL
+------++================+================++===================+
|      ||                |                ||                   |
| BODY ||   0x00: 0x00   |   0x01: SYNC   ||   0x30: DATA      |
|HEADER|| MP_INT: MP_INT | MP_INT: MP_INT || MP_INT: MP_OBJECT |
| SIZE ||                |                ||                   |
+------++================+================++===================+
 MP_INT                MP_MAP                      MP_MAP

Предполагается, что набор кортежей в ответе <data> будет представлять собой msgpack-массив кортежей, поскольку команда EVAL возвращается произвольный MsgPack-массив MP_ARRAY с произвольными MsgPack-значениями.

ОШИБКА: LEN + HEADER + BODY

0      5
+------++================+================++===================+
|      ||                |                ||                   |
| BODY ||   0x00: 0x8XXX |   0x01: SYNC   ||   0x31: ERROR     |
|HEADER|| MP_INT: MP_INT | MP_INT: MP_INT || MP_INT: MP_STRING |
| SIZE ||                |                ||                   |
+------++================+================++===================+
 MP_INT                MP_MAP                      MP_MAP

Где 0xXXX -- это код ошибки ERRCODE.

Сообщение об ошибке будет включено в ответ только в случае ошибки; предполагается, что значение <error> будет msgpack-строкой.

Удобные макросы для определения шестнадцатеричных постоянных для возвращаемых кодов можно найти по ссылке src/box/errcode.h

-- ключи для репликации
 <server_id>     ::= 0x02
 <lsn>           ::= 0x03
 <timestamp>     ::= 0x04
 <server_uuid>   ::= 0x24
 <cluster_uuid>  ::= 0x25
 <vclock>        ::= 0x26
-- коды для репликации
<join>      ::= 0x41
<subscribe> ::= 0x42
JOIN:

Сначала необходимо отправить изначальный запрос JOIN
               HEADER                      BODY
+================+================++===================+
|                |                ||   SERVER_UUID     |
|   0x00: 0x41   |   0x01: SYNC   ||   0x24: UUID      |
| MP_INT: MP_INT | MP_INT: MP_INT || MP_INT: MP_STRING |
|                |                ||                   |
+================+================++===================+
               MP_MAP                     MP_MAP

Затем экземпляр, к которому мы подключаемся, отправит последний файл снимка SNAP,
просто создав количество запросов вставки INSERT (с дополнительным LSN и ServerID)
(не отвечайте). Затем он отправит MP_MAP из vclock и закроет сокет.

+================+================++============================+
|                |                ||        +~~~~~~~~~~~~~~~~~+ |
|                |                ||        |                 | |
|   0x00: 0x00   |   0x01: SYNC   ||   0x26:| SRV_ID: SRV_LSN | |
| MP_INT: MP_INT | MP_INT: MP_INT || MP_INT:| MP_INT: MP_INT  | |
|                |                ||        +~~~~~~~~~~~~~~~~~+ |
|                |                ||               MP_MAP       |
+================+================++============================+
               MP_MAP                      MP_MAP

SUBSCRIBE:

Далее необходимо отправить запрос SUBSCRIBE:

                              HEADER
+===================+===================+
|                   |                   |
|     0x00: 0x42    |    0x01: SYNC     |
|   MP_INT: MP_INT  |  MP_INT: MP_INT   |
|                   |                   |
+===================+===================+
|    SERVER_UUID    |    CLUSTER_UUID   |
|   0x24: UUID      |   0x25: UUID      |
| MP_INT: MP_STRING | MP_INT: MP_STRING |
|                   |                   |
+===================+===================+
                 MP_MAP

      BODY
+================+
|                |
|   0x26: VCLOCK |
| MP_INT: MP_INT |
|                |
+================+
      MP_MAP

 Затем следует обработать каждый запрос, который пришел от других мастеров.
 Каждый запрос между мастерами получит дополнительный LSN и SERVER_ID.

Файлы форматов XLOG и SNAP выглядят практически одинаково. Заголовок выглядит следующим образом:

<type>\n                  SNAP\n или XLOG\n
<version>\n               в данный момент 0.13\n
Server: <server_uuid>\n   где UUID -- это 36-байтная строка
VClock: <vclock_map>\n    например, {1: 0}\n
\n

После файла заголовка идут кортежи с данными. Кортежи начинаются с маркера строки 0xd5ba0bab, а после последнего кортежа может стоять маркер конца файла 0xd510aded. Таким образом, между заголовком файла и маркером конца файла могут быть кортежи с данными в следующем виде:

0            3 4                                         17
+-------------+========+============+===========+=========+
|             |        |            |           |         |
| 0xd5ba0bab  | LENGTH | CRC32 PREV | CRC32 CUR | PADDING |
|             |        |            |           |         |
+-------------+========+============+===========+=========+
  MP_FIXEXT2    MP_INT     MP_INT       MP_INT      ---

+============+ +===================================+
|            | |                                   |
|   HEADER   | |                BODY               |
|            | |                                   |
+============+ +===================================+
    MP_MAP                     MP_MAP

См. пример в предыдущем разделе.

Чтобы поддерживать персистентность данных, Tarantool записывает каждый запрос изменения данных (insert, update, delete, replace, upsert) в файл журнала упреждающей записи (WAL-файл) в директорию wal_dir. Новый WAL-файл создается для количества записей, определенного в параметре rows_per_wal, или для количества байтов, указанного в wal_max_size . Каждому запросу на изменение данных присваивается постоянно возрастающее 64-битное число, представляющее собой регистрационный номер в журнале (LSN). Название WAL-файла состоит из LSN первой записи в файле плюс расширение .xlog.

Помимо номера записи в журнале (LSN) и запроса на изменение данных (в формате бинарного протокола Tarantool’а), каждая запись в WAL-файле содержит заголовок, некоторые метаданные, а также данные, форматированные по правилам msgpack. Например, так выглядит WAL-файл после первого запроса вставки INSERT («s:insert({1})») для базы данных из песочницы, созданной в упражнениях в «Руководстве для начинающих». Слева представлены шестнадцатеричные байты, которые можно просмотреть с помощью:

$ hexdump 00000000000000000000.xlog

а справа – комментарии.

Шестнадцатеричный дамп WAL-файла       Комментарий
--------------------       -------
58 4c 4f 47 0a             "XLOG\n"
30 2e 31 33 0a             "0.13\n" = version
53 65 72 76 65 72 3a 20    "Server: "
38 62 66 32 32 33 65 30 2d [Server UUID]\n
36 39 31 34 2d 34 62 35 35
2d 39 34 64 32 2d 64 32 62
36 64 30 39 62 30 31 39 36
0a
56 43 6c 6f 63 6b 3a 20    "Vclock: "
7b 7d                      "{}" = vclock value, initially blank
...                        (not shown = tuples for system spaces)
d5 ba 0b ab                Magic row marker always = 0xab0bbad5
19                         Length, not including length of header, = 25 bytes
00                           Record header: previous crc32
ce 8c 3e d6 70               Record header: current crc32
a7 cc 73 7f 00 00 66 39      Record header: padding
84                         msgpack code meaning "Map of 4 elements" follows
00 02                         element#1: tag=request type, value=0x02=IPROTO_INSERT
02 01                         element#2: tag=server id, value=0x01
03 04                         element#3: tag=lsn, value=0x04
04 cb 41 d4 e2 2f 62 fd d5 d4 element#4: tag=timestamp, value=an 8-byte "Float64"
82                         msgpack code meaning "map of 2 elements" follows
10 cd 02 00                   element#1: tag=space id, value=512, big byte first
21 91 01                      element#2: tag=tuple, value=1-element fixed array={1}

Для чтения файлов в формате .xlog в Tarantool’е предусмотрен модуль xlog.

Tarantool обрабатывает запросы атомарно: изменение либо принимается и записывается в WAL-файл, или полностью исключается. Проясним, как этом работает, используя в качестве примера REPLACE-запрос:

  1. Экземпляр сервера пытается найти оригинальный кортеж по первичному ключу. Если кортеж найден, ссылка на него сохраняется для дальнейшего использования.
  2. Происходит проверка нового кортежа. Например, если в нем нет проиндексированного поля, или же тип проиндексированного поля не совпадает с типом в определении индекса, изменение прерывается.
  3. Новый кортеж заменяет старый кортеж во всех существующих индексах.
  4. В процесс записи, запущенный в потоке журнала упреждающей записи, отправляется сообщение о необходимости внесения записи в WAL-файл. Экземпляр переключается на работу со следующим запросом, пока запись не будет подтверждена.
  5. При успешном выполнении на клиент отправляется подтверждение. В случае ошибки начинается процедура отката. Во время процедуры отката поток обработки транзакций откатывается все изменения в базу данных, которые произошли после первого невыполненного изменения, от последнего с первому, вплоть до первого невыполненного изменения. Все запросы, которые подверглись откату, прерываются с ошибкой ER_WAL_IO. Новые изменения не применяются во время отката. По окончании процедуры отката сервер повторно запускает конвейер обработки операций.

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

Поток обработки транзакций взаимодействует с потоком записи в журнал упреждающей записи с помощью асинхронного (однако надежного) обмена сообщениями. Поток обработки транзакций, который не блокируется при задачах записи в журнал, продолжает быстро обрабатывать запрос даже при большом объеме дискового ввода-вывода. Ответ на запрос отправляется по готовности, даже если ранее на том же соединении были незавершенные запросы. В частности, на производительность выборки не влияет загрузка диска, даже если SELECT-запросы передаются вместе с запросами UPDATE и DELETE.

При записи в WAL можно применять различные режимы долговечности, что определяет конфигурационная переменная wal_mode. Можно полностью отключить журнал упреждающей записи, присвоив wal_mode значение none. Даже без журнала упреждающей записи возможно сделать персистентную копию всего набора данных с помощью запроса box.snapshot().

Файл в формате .xlog всегда содержит изменения на основании первичного ключа. Даже если клиент запрашивает обновление или удаление по вторичному ключу, запись в файле в формате .xlog будет содержать первичный ключ.

Формат файла снимка .snap практически такой же, что и формат WAL-файла .xlog. Тем не менее, заголовок снимка отличается: он содержит глобально уникальный идентификатор экземпляра и положения файла снимка в истории относительно более ранних файлов снимка. Кроме того, отличается содержание: .xlog-файл может содержать записи о любых запросах изменения данных (вставка, обновление, обновление и вставка и удаление), а .snap-файл может содержать лишь записи о вставках в спейсы memtx’а.

В первую очередь записи в .snap-файле упорядочены по идентификатору спейса. Таким образом, записи в системные спейсы – такие как _schema, _space, _index, _func, _priv и _cluster – будут находиться в начале .snap-файла до записей в другие спейсы, созданные пользователями.

Во вторую очередь записи в .snap-файле упорядочены по первичному ключу.

Процесс восстановления начинается, когда box.cfg{} впервые используется после запуска экземпляра Tarantool-сервера.

Процесс восстановления должен восстановить базы данных на момент последнего отключения экземпляра. Для этого можно использовать последний файл снимка и любые WAL-файлы, которые были записаны после создания снимка. Ситуацию осложняет фактор того, что в Tarantool’е используются два движка – данные memtx’а должны быть реконструированы полностью из снимка и WAL-файлов, тогда как данные vinyl’а будут находиться на диске, но может потребоваться их обновление на время создания контрольной точки. (При создании снимка Tarantool передает движку vinyl команду создания контрольной точки, а операция создания снимка откатывается в случае какой-либо ошибки, поэтому контрольная точка vinyl’а будет настолько же актуальной, как и файл снимка.)

Шаг 1
Выполнить чтение конфигурационных параметров из запроса box.cfg{}. Параметры, которые могут повлиять на восстановление: work_dir, wal_dir, memtx_dir, vinyl_dir и force_recovery.
Шаг 2

Найти последний файл снимка. Использовать данные для реконструкции in-memory баз данных. Передать команду vinyl’у о восстановлении до последней контрольной точки.

На самом деле, есть два варианта реконструкции баз данных memtx’а в зависимости от того, выполняется ли стандартная процедура.

Если выполняется стандартная процедура (force_recovery = false), memtx может выполнить чтение данных из снимка с отключенными индексами. Сначала все кортежи считываются в память. Затем происходит массовая загрузка первичных ключей с учетом того, что данные уже отсортированы по первичному ключу в каждом спейсе.

Если выполняется нестандартная процедура принудительного восстановления (force_recovery = true), Tarantool проводит дополнительную проверку. Сначала индексы активны, и кортежи добавляются по одному. Это означает, что будут выявлены любые нарушения ограничений уникальности ключей, и все повторяющиеся значения пропускаются. Как правило, не будет нарушений ограничений или повторяющихся значений, поэтому такие проверки проводятся только в случае ошибки.

Шаг 3
Найти WAL-файл, который был создан во время создания файла снимка или позже. Выполнить чтение записей журнала до тех пор, пока LSN записи в журнале не будет больше LSN снимка или больше LSN контрольной точки в vinyl’е. Это и будет начальной точкой для процесса восстановления, которая соответствует текущему состоянию движков.
Шаг 4
Повторить записи журнала с начальной точки до конца WAL. Движок пропускает команду повторения, если данные старше контрольной точки движка.
Шаг 5
Повторно создать все вторичные индексы для движка memtx.
Нашли ответ на свой вопрос?
Обратная связь