Top.Mail.Ru
Дополнительные типы MessagePack | Tarantool
Tarantool
Узнайте содержание релиза 2.8
Справочники Детали реализации Дополнительные типы MessagePack

Дополнительные типы MessagePack

Дополнительные типы MessagePack

Tarantool использует предопределенные дополнительные типы MessagePack для представления некоторых специальных значений. Дополнительные типы включают MP_DECIMAL, MP_UUID и MP_ERROR. Эти типы требуют особого внимания со стороны разработчиков коннекторов, так как должны рассматриваться отдельно от типов MessagePack по умолчанию и корректно приводиться к типам языков программирования.

Тип MessagePack EXT MP_EXT вместе с типом расширения MP_DECIMAL является заголовком для значений типа DECIMAL.

MP_DECIMAL – это 1.

Спецификация MessagePack определяет два вида типов:

  • типы fixext 1/2/4/8/16 имеют фиксированную длину, поэтому длина не кодируется явно;
  • типы ext 8/16/32 требуют кодирования длины данных.

MP_EXP + (не обязательно) length подразумевает использование одного из этих типов.

Десятичное представление MessagePack выглядит следующим образом:

+--------+-------------------+------------+===============+
| MP_EXT | length (optional) | MP_DECIMAL | PackedDecimal |
+--------+-------------------+------------+===============+

Здесь length – это длина поля PackedDecimal, и оно имеет тип MP_UINT, когда кодируется явно (т.е. когда тип – ext 8/16/32).

PackedDecimal имеет следующую структуру:

 <--- длина в байтах -->
+-------+=============+
| scale |     BCD     |
+-------+=============+

Здесь scale – это либо MP_INT, либо MP_UINT.
scale = количество цифр после запятой.

BCD - это последовательность байтов, обозначающая десятичные цифры кодируемого числа (каждый байт имеет две десятичные цифры, каждая из которых кодируется с использованием 4-битных nibbles), поэтому byte >> 4 - первая цифра, а byte & 0x0f - вторая цифра. Самая левая цифра в массиве - самая значимая. Самая правая цифра в массиве - наименее значимая.

Первый байт BCD-массива содержит первую цифру числа, представленную следующим образом:

|  4 bits           |  4 bits           |
   = 0x                = the 1st digit

(Первый nibble содержит 0, если десятичное число имеет четное число цифр). Последний байт BCD-массива содержит последнюю цифру числа и последний nibble, представленный следующим образом:

|  4 bits           |  4 bits           |
   = the last digit    = nibble

Последний nibble обозначает знак числа:

  • 0x0a, 0x0c, 0x0e, 0x0f обозначают плюс,
  • 0x0b and 0x0d обозначают минус.

Примеры

Десятичное число -12.34 будет закодировано как 0xd6,0x01,0x02,0x01,0x23,0x4d:

|MP_EXT (fixext 4) | MP_DECIMAL | scale |  1   |  2,3 |  4 (minus) |
|       0xd6       |    0x01    | 0x02  | 0x01 | 0x23 | 0x4d       |

Десятичное число 0.000000000000000000000000000000000010 будет закодировано как 0xc7,0x03,0x01,0x24,0x01,0x0c:

| MP_EXT (ext 8) | length | MP_DECIMAL | scale |  1   | 0 (plus) |
|      0xc7      |  0x03  |    0x01    | 0x24  | 0x01 | 0x0c     |

The MessagePack EXT type MP_EXT together with the extension type MP_UUID for values of the UUID type. Since version 2.4.1.

MP_UUID – это 2.

Спецификация MessagePack определяет d8 как fixext с размером 16, а размер uuid всегда равен 16. Таким образом, представление uuid MessagePack выглядит следующим образом:

+--------+------------+-----------------+
| MP_EXT | MP_UUID    | UuidValue       |
| = d8   | = 2        | = 16-byte value |
+--------+------------+-----------------+

16-байтовое значение имеет 2 цифры на байт. Обычно оно состоит из 11 полей, которые кодируются от большего к меньшему беззнаковыми целыми числами в следующем порядке: time_low (4 байта), time_mid (2 байта), time_hi_and_version (2 байта), clock_seq_hi_and_reserved (1 байт), clock_seq_low (1 байт), node[0], …, node[5] (по 1 байту).

Некоторые функции в модуле uuid могут выдавать значения, совместимые с типом данных UUID. Например, после

uuid = require('uuid')
box.schema.space.create('t')
box.space.t:create_index('i', {parts={1,'uuid'}})
box.space.t:insert{uuid.fromstr('f6423bdf-b49e-4913-b361-0740c9702e4b')}
box.space.t:select()

пакет ответа сервера покажет, что он содержит

d8 02 f6 42 3b df b4 9e 49 13 b3 61 07 40 c9 70 2e 4b

Начиная с версии 2.4.1, в ответах на ошибки содержится дополнительная информация, соответствующая описанию в разделе Бинарный протокол — ответы на ошибки. Это «совместимое» улучшение, потому что клиенты, которые ожидают ответы сервера старого образца, должны игнорировать компоненты ассоциативного массива, которые они не распознают. Обратите внимание, что константа IPROTO_ERROR в ./box/iproto_constants.h была 0x31, а теперь IPROTO_ERROR – 0x52, а IPROTO_ERROR_24 – 0x31.

++=========================+============================+
||                         |                            |
||   0x31: IPROTO_ERROR_24 |   0x52: IPROTO_ERROR                                 |
|| MP_INT: MP_STRING       | MP_MAP: дополнительная информация  |
||                         |                            |
++=========================+============================+
                        MP_MAP

Дополнительная информация, большая часть которой также хранится в полях объекта ошибки:

MP_ERROR_TYPE (0x00) (MP_STR) Тип, подразумевающий источник, как в error_object.base_type, например «ClientError».

MP_ERROR_FILE (0x01) (MP_STR) Файл с исходным кодом, в котором была перехвачена ошибка, как в error_object.trace.

MP_ERROR_LINE (0x02) (MP_UINT) Номер строки в файле исходных кодов, как в error_object.trace.

MP_ERROR_MESSAGE (0x03) (MP_STR) Текст причины, как в error_object.message. Значение здесь будет таким же, как и значение IPROTO_ERROR_24

MP_ERROR_ERRNO (0x04) (MP_UINT) Порядковый номер ошибки, как в error_object.errno. Не путать с MP_ERROR_ERRCODE.

MP_ERROR_ERRCODE (0x05) (MP_UINT) Номер ошибки, как в файле errcode.h, как в error_object.code, который также можно получить функцией C box_error_code(). Значение здесь будет таким же, как и в нижней части значения Response-Code-Indicator.

MP_ERROR_FIELDS (0x06) (MP_MAPs) Дополнительные поля в зависимости от типа ошибки. Например, если MP_ERROR_TYPE имеет значение «AccessDeniedError», то MP_ERROR_FIELDS будет включать «object_type», «object_name», «access_type». При отсутствии дополнительных полей это поле будет пропущено в теле ответа.

Разработчики клиента и коннекторов должны убедиться, что неизвестные ключи ассоциативных массивов игнорируются, а также проверить наличие новых ключей в файле исходного кода Tarantool, в котором определено создание объекта ошибки. В версии 2.4.1 имя этого файла с исходным кодом mp_error.cc.

Например, в версии 2.4.1 или более поздней, если мы попытаемся создать дубликат пробела с помощью команды
conn:eval([[box.schema.space.create('_space');])),
ответ сервера будет выглядеть так:

ce 00 00 00 88                  MP_UINT = HEADER + BODY SIZE
83                              MP_MAP, size 3 (i.e. 3 items in header)
  00                              Response-Code-Indicator
  ce 00 00 80 0a                  MP_UINT = hexadecimal 800a
  01                              IPROTO_SYNC
  cf 00 00 00 00 00 00 00 05      MP_UINT = sync value
  05                              IPROTO_SCHEMA_VERSION
  ce 00 00 00 4e                  MP_UINT = schema version value
82                              MP_MAP, size 2
  31                              IPROTO_ERROR_24
  bd 53 70 61 63 etc.             MP_STR = "Space '_space' already exists"
  52                              IPROTO_ERROR
  81                              MP_MAP, size 1
    00                              MP_ERROR_STACK
    91                              MP_ARRAY, size 1
      86                              MP_MAP, size 6
        00                              MP_ERROR_TYPE
        ab 43 6c 69 65 6e 74 etc.       MP_STR = "ClientError"
        02                              MP_ERROR_LINE
        cd                              MP_UINT = line number
        01                              MP_ERROR_FILE
        aa 01 b6 62 75 69 6c etc.       MP_STR "builtin/box/schema.lua"
        03                              MP_ERROR_MESSAGE
        bd 53 70 61 63 65 20 etc.       MP_STR = Space.'_space'.already.exists"
        04                              MP_ERROR_ERRNO
        00                              MP_UINT = error number
        05                              MP_ERROR_ERRCODE
        0a                              MP_UINT = eror code ER_SPACE_EXISTS