Top.Mail.Ru
Tarantool » latest » Справочники » Детали реализации » Дополнительные типы MessagePack
 
Справочники / Детали реализации / Дополнительные типы MessagePack
Справочники / Детали реализации / Дополнительные типы MessagePack

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

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

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

Тип DECIMAL

Тип 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     |

Тип UUID

MessagePack EXT тип MP_EXT вместе с дополнительным типом MP_UUID предназначен для значений типа UUID.

MP_UUID – это 2.

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

+--------+------------+-----------------+
| MP_EXT | MP_UUID    | PackedDecimal   |
| = 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

Тип ERROR

Начиная с версии 2.4.1, в ответах на ошибки содержится дополнительная информация, соответствующая описанию в разделе Box protocol – responses for errors. Это «совместимое» улучшение, потому что клиенты, которые ожидают ответы сервера старого образца, должны игнорировать компоненты ассоциативного массива, которые они не распознают. Обратите внимание, что константа 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: extra information  |
||                         |                            |
++=========================+============================+
                        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