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

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

Tarantool использует предопределенные дополнительные типы MessagePack для представления некоторых специальных значений. Дополнительные типы включают MP_DECIMAL, MP_UUID,``MP_ERROR``, MP_DATETIME, and MP_INTERVAL. Эти типы требуют особого внимания со стороны разработчиков коннекторов, так как должны рассматриваться отдельно от типов 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     |

Тип MessagePack EXT MP_EXT вместе с типом расширения MP_UUID является заголовком для значений типа UUID. Доступно с версии 2.4.1.

MP_UUID – это 2.

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

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

The 16-byte value has 2 digits per byte. Typically, it consists of 11 fields, which are encoded as big-endian unsigned integers in the following order:

  • time_low (4 bytes)
  • time_mid (2 bytes)
  • time_hi_and_version (2 bytes)
  • clock_seq_hi_and_reserved (1 byte)
  • clock_seq_low (1 byte)
  • node[0], …, node[5] (1 byte each)

Некоторые функции в модуле 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_ERROR0x52, а IPROTO_ERROR_240x31.

MP_ERROR – это 3.

++=========================+============================+
||                         |                            |
||   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

Since version 2.10.0. The MessagePack EXT type MP_EXT together with the extension type MP_DATETIME is a header for values of the DATETIME type. It creates a container with a payload of 8 or 16 bytes.

MP_DATETIME type is 4.

The MessagePack specification defines d7 to mean fixext with size 8 or d8 to mean fixext with size 16.

So the datetime MessagePack representation looks like this:

+---------+----------------+==========+-----------------+
| MP_EXT  | MP_DATETIME    | seconds  | nsec; tzoffset; |
| = d7/d8 | = 4            |          | tzindex;        |
+---------+----------------+==========+-----------------+

MessagePack data contains:

  • Seconds (8 bytes) as an unencoded 64-bit signed integer stored in the little-endian order.
  • The optional fields (8 bytes), if any of them have a non-zero value. The fields include nsec, tzoffset, and tzindex packed in the little-endian order.

For more information about the datetime type, see datetime field type details and reference for the datetime module.

Since version 2.10.0. The MessagePack EXT type MP_EXT together with the extension type MP_INTERVAL is a header for values of the INTERVAL type.

MP_INTERVAL type is 6.

The interval is saved as a variant of a map with a predefined number of known attribute names. If some attributes are undefined, they are omitted from the generated payload.

The interval MessagePack representation looks like this:

+--------+-------------------------+-------------+----------------+
| MP_EXT | Size of packed interval | MP_INTERVAL | PackedInterval |
+--------+-------------------------+-------------+----------------+

Packed interval consists of:

  • Packed number of non-zero fields.
  • Packed non-null fields.

Each packed field has the following structure:

+----------+=====================+
| field ID |     field value     |
+----------+=====================+

The number of defined (non-null) fields can be zero. In this case, the packed interval will be encoded as integer 0.

List of the field IDs:

  • 0 – year
  • 1 – month
  • 2 – week
  • 3 – day
  • 4 – hour
  • 5 – minute
  • 6 – second
  • 7 – nanosecond
  • 8 – adjust

Example

Interval value 1 years, 200 months, -77 days is encoded in the following way:

tarantool> I = datetime.interval.new{year = 1, month = 200, day = -77}
---
...

tarantool> I
---
- +1 years, 200 months, -77 days
...

tarantool> M = msgpack.encode(I)
---
...

tarantool> M
---
- !!binary xwsGBAABAczIA9CzCAE=
...

tarantool> tohex = function(s) return (s:gsub('.', function(c) return string.format('%02X ', string.byte(c)) end)) end
---
...

tarantool> tohex(M)
---
- 'C7 0B 06 04 00 01 01 CC C8 03 D0 B3 08 01 '
...

Where:

  • C7 – MP_EXT
  • 0B – size of a packed interval value (11 bytes)
  • 06 – MP_INTERVAL type
  • 04 – number of defined fields
  • 00 – field ID (year)
  • 01 – packed value 1
  • 01 – field ID (month)
  • CCC8 – packed value 200
  • 03 – field ID (day)
  • D0B3 – packed value -77
  • 08 – field ID (adjust)
  • 01 – packed value 1 (DT_LIMIT)

For more information about the interval type, see interval field type details and description of the datetime module.

Нашли ответ на свой вопрос?
Обратная связь