Перейти к содержанию

tarantool: Библиотека для работы с tarantool из nginx с встроенным Lua модулем или с nginx-module-lua

Установка

Если вы еще не настроили подписку на RPM репозиторий, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.

CentOS/RHEL 7 или Amazon Linux 2

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-tarantool

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-tarantool

Чтобы использовать эту Lua библиотеку с NGINX, убедитесь, что nginx-module-lua установлен.

Этот документ описывает lua-resty-tarantool v0.3, выпущенную 21 октября 2015 года.


Введение

Это библиотека для подключения к tarantool NoSQL базе данных. Эта база данных имеет очень интересные функции, которые делают ее своего рода мостом между традиционной SQL базой данных и документно-ориентированными хранилищами, такими как CouchDB.

Это форк другого проекта, который меня не устраивал. Он обильно документирован и обновляется в соответствии с API tarantool. Особенно с поддержкой команды upsert.

Еще одна вещь, о которой стоит помнить, это то, что библиотека старается быть последовательной между тем, как команды update и upsert вызываются в консоли с использованием Lua, и тем, как работает API. В частности, номера полей. В консоли номер поля учитывает наличие первичного индекса как первого поля. Следовательно, любое поле, которое идет после, будет иметь позицию, которая это учитывает. В частности, при указании операторов, которые нужно использовать для операций update или upsert.

Использование

Создание соединения

local tar = require 'resty.tarantool'

local tar, err = tnt:new({
    host = '127.0.0.1',
    port = 3301,
    user = 'luser',
    password = 'some password',
    socket_timeout = 2000,
})
Выше создается объект соединения, который подключается к экземпляру сервера tarantool, работающему на локальном хосте на порту 3301, для пользователя luser с паролем some password. См. Руководство Tarantool по аутентификации для получения подробной информации о том, как настраивать пользователей и назначать им привилегии.

Таймаут сокета (прием и отправка) составляет 2 секунды (2000 мс).

set_timeout

settimeout(<объект соединения>, <таймаут в мс>)

Устанавливает как таймауты отправки, так и получения в миллисекундах для данного сокета.

tnt:set_timeout(5000) -- 5s таймаут для операций отправки/получения

Функция возвращает true, если установка прошла успешно, nil, если нет. Обратите внимание, что для того, чтобы таймаут вступил в силу, эту функцию необходимо вызвать до установления соединения, т.е. до вызова функции connect. В качестве альтернативы таймаут можно указать при создании объекта соединения (cosocket).

connect

connect(<объект соединения>)

Подключает сокет, созданный выше, к порту и адресу, указанным при создании объекта соединения.

tar:connect()
Функция возвращает true, если соединение прошло успешно, nil, если нет.

set_keepalive

set_keepalive(<объект соединения>)

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

tar:set_keepalive()

Функция возвращает true, если сокет успешно помещен в пул соединений (установлен keepalive). nil, если нет.

disconnect

disconnect(<объект соединения>)

Закрывает соединение с данным сервером tarantool, работающим по указанному адресу и порту.

tar:disconnect()

Функция возвращает true, если соединение успешно закрыто. nil, если нет.

ping

Команда ping полезна для мониторинга сервера tarantool, чтобы проверить, доступен ли он. Если он доступен для запросов, возвращает строку PONG.

tar:ping()
-- возвращает PONG

select

Операция select запрашивает данную базу данных (пространство) для извлечения записей.

select(<объект соединения>, <имя пространства>, <индекс>, <ключ>, <опции>)

где <опции> является необязательным аргументом, который может состоять из таблицы, которая может иметь следующие ключи:

  • offset: количество записей, которые нужно пропустить при выполнении запроса.
  • limit: максимальное количество записей для возврата.
  • iterator: число, указывающее, какой итератор использовать. Указывается таблицей:

local iterator_keys = {
  EQ = 0, -- равенство
  REQ = 1, -- обратное равенство
  ALL = 2, -- все кортежи в индексе
  LT = 3, -- меньше чем
  LE = 4, -- меньше или равно
  GE = 5, -- больше или равно
  GT = 6, -- больше чем
  BITSET_ALL_SET = 7, -- биты в битовой маске все установлены
  BITSET_ANY_SET = 8, -- любой из битов в битовой маске установлен
  BITSET_ALL_NOT_SET = 9, -- ни один из битов в битовой маске не установлен
}
Более подробную информацию об итераторах см. в руководстве tarantool.

Примеры select

Запрос пространства _space (БД) для получения идентификатора пространства _index.

local res, err = tar:select('_space', 'name', '_index')

-- ответ:
[2881,"_index","memtx",0,"",
  [{"name":"id","type":"num"},
   {"name":"iid","type":"num"},
   {"name":"name","type":"str"},
   {"name":"type","type":"str"},
   {"name":"opts","type":"array"},
   {"name":"parts","type":"array"}]]]
Вышеуказанный запрос эквивалентен запросу в консоли:

box.space._space.index.name:select{ '_index' }

Запрос пространства 'activities' для получения активностей с price меньше 300

-- Примечание. price является индексом пространства activities.
local res, err = tar:select('activities', 'price', 300, { iterator = 'LT' })
Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities.index.price:select({ 300 }, { iterator = 'LT' })

insert

insert(<объект соединения>, <имя пространства>, <кортеж>)

где <кортеж> — это кортеж, который нужно вставить в <пространство>, устанавливая первичный индекс, который уникален, на значение, указанное в кортеже.

Функция возвращает вставленную запись, если операция прошла успешно.

Примеры insert

local res, err = tar:insert('activities', { 16, 120, { activity = 'surf', price = 121 } })

-- ответ:
[[16,120,{"activity":"surf","price":121}]]
Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities:insert({16, 120, { activity = 'surf', price = 121 }})
16 — это значение первичного индекса здесь. Это означает, что для индекса типа integer это будет запись с первичным индексом 16.

replace

replace(<объект соединения>, <имя пространства>, <кортеж>)

Команда replace аналогична по вызову и сигнатуре команде insert. Но теперь мы ищем замену записи, которая уже существует, вместо вставки новой. Нам снова нужно значение первичного уникального индекса. Но теперь значение должно существовать, чтобы операция прошла успешно. Если операция проходит успешно, возвращается запись с замененными значениями.

Примеры replace

local res, err = tar:replace('activities', { 16, 120, { activity = 'surf', price = 120 } })
-- ответ:
[[16,120,{"activity":"surf","price":120}]]
Здесь мы заменяем прежнюю цену 121 на 120. Значение первичного индекса, 16, соответствует записи, которую мы вставили выше.

Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities:update({ 16, 120, { activity = 'surf', price = 120 }})

update

update(<объект соединения>, <имя пространства>, <индекс>, <ключ>, <список операторов>)

где <список операторов> — это список операторов, как указано в руководстве tarantool. Пара (<индекс>, <ключ>) уникально идентифицирует запись, т.е. <ключ> — это значение первичного (уникального) <индекса>.

<список операторов> — это таблица следующего вида:

{ <оператор>, <позиция поля>, <значение> }
операторы:

  • + для добавления к числовому полю.
  • - для вычитания из числового поля.
  • & для побитовой операции AND между двумя беззнаковыми целыми числами.
  • | для побитовой операции OR между двумя беззнаковыми целыми числами.
  • ^ для побитовой операции XOR между двумя беззнаковыми целыми числами.
  • : для слияния строк.
  • ! для вставки поля.
  • # для удаления поля.
  • = для присвоения заданного значения полю.

возвращает обновленную запись, если операция успешна.

Примеры update

local res, err = tar:update('activities', 'primary', 16, { { '=', 2, 341 }, { '=', 3,  { activity = 'kitesurfing', price = 341 }}} )
-- ответ:
[16,341,{"activity":"kitesurfing","price":341}]]
Запись с индексом primary 16, которую мы вставили выше, была обновлена.

Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities.index.primary({ 16 }, { { '=', 2, 341 }, { '=', 3,  { activity = 'kitesurfing', price = 341 }}})

upsert

upsert(<объект соединения>, <имя пространства>, <ключ>, <список операторов>, <новый кортеж>)

Помимо аргумента <новый кортеж>, сигнатура функции аналогична update. На самом деле upsert — это две команды в одной. update, если запись, указанная парой (<индекс>, <ключ>), существует, и insert, если нет. Ключ — это значение из первичного индекса, т.е. уникально. <новый кортеж> — это кортеж, который будет вставлен, если значение <ключа> не существует в <индексе>. Возвращает пустую таблицу {}, если операция успешна. Если операция не удалась, возвращает nil.

Примеры upsert

insert.

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 17, 450, { activity = 'waterski', price = 365 }})
-- ответ:
{}
Мы вставили новую запись с ключом 17 для первичного индекса из кортежа:

{ 18, 450, { activity = 'waterski', price = 365 }}
Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities:upsert({ 17 }, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 17, 450, { activity = 'waterski', price = 365 }})
update.

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 18, 285, { activity = 'kitesurfing', price = 285 }})
-- ответ:
{}
Теперь мы выполняем обновление записи, идентифицированной ключом 17 в индексе primary (уникальном).

delete

delete(<объект соединения>, <пространство>, <ключ>)

удаляет запись, уникально указанную <ключом>, из <пространства>. Обратите внимание, что <ключ> должен принадлежать первичному (уникальному) индексу. Возвращает удаленную запись, если операция успешна.

Примеры delete

local response, err = tar:delete('activities', 17)
-- ответ:
[17,450,{"activity":"waterski","price":365}]]
Мы удалили запись, уникально идентифицированную ключом 17 в первичном индексе из пространства activities.

Вышеуказанный запрос эквивалентен запросу в консоли:

box.space.activities:delete({ 17 })

call

call(<объект соединения>, <proc>, <args>)

Вызывает хранимую процедуру (функцию Lua) на сервере tarantool, к которому мы подключены. Возвращает результаты вызова.

Примеры call

Поскольку консоль tarantool является Lua REPL, любую функцию можно вызвать, если она доступна в окружении.

local res, err = tar:call('table.concat', { {'hello', ' ', 'world' } })
-- ответ:
[["hello world"]]
Мы вызвали функцию table.concat из библиотеки table для объединения таблицы:

{'hello', ' ', 'world' }
Вышеуказанный запрос эквивалентен запросу в консоли:

table.concat({'hello', ' ', 'world' })

Для множества примеров хранимых процедур tarantool смотрите репозиторий; https://github.com/mailru/tarlua

hide_version_header

hide_version_header(<объект соединения>)

По умолчанию каждый ответ отправляет пользовательский HTTP заголовок X-Tarantool-Version с версией сервера tarantool.

X-Tarantool-Version: 1.6.6-191-g82d1bc3

Вызов hide_version_header удаляет заголовок.

tar:hide_version_header()

Он не возвращает значений.

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-tarantool.