Версия:

Модуль net.box

Модуль net.box

Общие сведения

Модуль net.box включает в себя коннекторы для удаленных систем с базами данных. Одним из вариантов, который рассматривается позднее, является подключение к MySQL, MariaDB или PostgreSQL (см. справочник по Модулям СУБД SQL). Другим вариантом, который рассматривается в данном разделе, является подключение к экземплярам Tarantool-сервера по сети.

Можно вызвать следующие методы:

  • require('net.box') для получения объекта net.box (который называется net_box для примеров в данном разделе),
  • net_box.connect() для подключения и получения объекта подключения (который называется conn для примеров в данном разделе),
  • другие процедуры net.box(), передающие conn: для выполнения запросов в удаленной системе базы данных,
  • conn:close для отключения.

Все методы net.box безопасны для файберов, то есть можно безопасно обмениваться и использовать один и тот же объект подключения в нескольких файберах одновременно. Фактически так лучше всего работать в Tarantool’е. Когда несколько файберов используют одно соединение, все запросы передаются по одному сетевому сокету, но каждый файбер получает правильный ответ. Уменьшение количества активных сокетов снижает затрату ресурсов на системные вызовы и увеличивает общую производительность сервера. Однако, в некоторых случаях отдельного соединения недостаточно – например, когда необходимо отдавать приоритет разным запросам или использовать различные идентификаторы при аутентификации.

В большинстве методов net.box можно использовать заключительный аргумент {options}, который может быть:

  • {timeout=...}. Например, метод с заключительным аргументом {timeout=1.5} остановится через 1,5 секунды на локальном узле, хотя это не гарантирует, что выполнение остановится на удаленном сервере.
  • {buffer=...}. Например, см. модуль buffer.
  • {is_async=...}. Например, метод с заключительным аргументом {is_async=true} не будет ждать результата выполнения запроса. См. описание is_async.
  • {on_push=... on_push_ctx=...}. Для получения внеполосных сообщений. См. описание box.session.push.

На диаграмме ниже представлены возможные состояния и варианты перехода из одного состояния в другое:

net_states.svg

На этой диаграмме:

  • Работа начинается с начального состояния „initial“.
  • Выполнение метода net_box.connect() переводит состояние в „connecting“, создается рабочий файбер.
  • Если требуются аутентификация и загрузка схемы, можно позднее повторно войти в состояние загрузки схемы „fetch_schema“ из активного „active“, если запрос не будет выполнен из-за ошибки несовпадения версий схемы, то есть будет вызвана перезагрузка схемы.
  • Метод conn.close() изменяет состояние на закрытое „closed“ и отключает рабочий процесс. Если транспорт уже находится в состоянии ошибки „error“, close() не делает ничего.

Индекс

Ниже приведен перечень всех функций модуля net.box.

Имя Использование
net_box.connect()
net_box.new()
Создание подключения
conn:ping() Выполнение команды проверки состояния PING
conn:wait_connected() Ожидание активности или закрытия подключения
conn:is_connected() Проверка активности или закрытия подключения
conn:wait_state() Ожидание нужного состояния
conn:close() Закрытие подключения
conn.space.space-name:select{field-value} Выбор одного или более кортежей
conn.space.space-name:get{field-value} Выбор кортежа
conn.space.space-name:insert{field-value} Вставка кортежа
conn.space.space-name:replace{field-value} Вставка или замена кортежа
conn.space.space-name:update{field-value} Обновление кортежа
conn.space.space-name:upsert{field-value} Обновление кортежа
conn.space.space-name:delete{field-value} Удаление кортежа
conn:eval() Оценка и выполнение выражения в строке
conn:call() Вызов хранимой процедуры
conn:timeout() Установка времени ожидания
net_box.connect(URI[, {option[s]}])
net_box.new(URI[, {option[s]}])

Примечание

Имена connect() и new() являются синонимами: предпочтительным будет connect(), а new() обеспечивает поддержку обратной совместимости.

Создание нового подключения. Подключение устанавливается по требованию во время первого запроса. Можно повторно установить подключение автоматически после отключения (см. ниже опцию reconnect_after). Возвращается объект conn, который поддерживает методы создание удаленных запросов, таких как select, update или delete.

Для локального Tarantool-сервера есть заданный объект всегда установленного подключения под названием net_box.self. Он создан с целью облегчить полиморфное использование API модуля net_box. Таким образом, conn = net_box.connect('localhost:3301') можно заменить на conn = net_box.self. Однако, есть важно отличие встроенного подключения от удаленного. При встроенном подключении запросы без изменения данных не передают управление. При использовании удаленного подключения любой запрос может передавать управление исходя из правил неявной передачи управления, и состояние базы данных может измениться к тому времени, как управление вернется.

Возможные опции

  • wait_connected: по умолчанию, создание подключения блокируется до тех пор, пока подключение не будет установлено, но передача wait_connected=false заставит метод сразу же вернуться. Передача времени ожидания заставит метод ждать до возвращения (например, wait_connected=1.5 заставит ожидать подключения максимум 1,5 секунды).

    Примечание

    Если присутствует reconnect_after, wait_connected проигнорирует неустойчивые отказы. Ожидание заканчивается, когда подключение установлено или явным образом закрыто.

  • reconnect_after: net.box автоматически подключается повторно в случае разрыва соединения или провала попытки подключения. В таком случае неустойчивые сетевые отказы становятся очевидными. Повторное подключение выполняется автоматически в фоновом режиме, поэтому запросы/обращения, не выполненные по причине потери соединения, явным образом выполняются повторно. Количество повторов не ограничено, попытки подключения выполняются в течение указанного времени ожидания (например, reconnect_after=5 – 5 секунд). После явного закрытия подключения или удаления сборщиком мусора в Lua попытки соединения повторно не выполняются.

  • call_16: [с 1.7.2] по умолчанию, подключения net.box соответствуют команде CALL нового бинарного протокола, который не поддерживает обратную совместимость с предыдущими версиями. Команда нового бинарного протокола для вызова CALL больше не ограничивает функцию в возврате массива кортежей и позволяет возвращать произвольный результат в формате MsgPack/JSON, включая scalar (скалярные значения), nil (нулевые значения) и void (пусто). Старый метод CALL оставлен нетронутым для обратной совместимости. В следующей основной версии он будет удален. Все драйверы для языков программирования будут постепенно переведены на использование нового метода CALL. Для подключения к экземпляру Tarantool’а, в котором используется старый метод CALL, укажите call_16=true.

  • console: в зависимости от значения параметра поддерживаются различные методы (как если бы возвращались экземпляры разных классов). Если console = true, можно использовать методы conn: close(), is_connected(), wait_state(), eval() (в этом случае поддерживаются и бинарный сетевой протокол, и протокол Lua-консоли). Если console = false (по умолчанию), также можно использовать методы conn для работы с базой данных (в этом случае поддерживается только бинарный протокол). Устарел: console = true объявлен устаревшим, вместо него следует использовать console.connect().

  • connect_timeout: количество секунд ожидания до возврата ошибки «error: Connection timed out».

Параметры:
  • URI (string) – URI объекта подключения
  • options – возможные опции: wait_connected, reconnect_after, call_16 и console
возвращается:

объект подключения

тип возвращаемого значения:
 

пользовательские данные

Примеры:

conn = net_box.connect('localhost:3301')
conn = net_box.connect('127.0.0.1:3302', {wait_connected = false})
conn = net_box.connect('127.0.0.1:3303', {reconnect_after = 5, call_16 = true})
object conn
conn:ping([options])

Выполнение команды проверки состояния PING.

Параметры:
  • options (table) – the supported option is timeout=seconds
возвращается:

true (правда), если выполнено, false (ложь) в случае ошибки

тип возвращаемого значения:
 

boolean (логический)

Пример:

net_box.self:ping({timeout = 0.5})
conn:wait_connected([timeout])

Ожидание активности или закрытия подключения.

Параметры:
  • timeout (number) – в секундах
возвращается:

true (правда) при подключении, false (ложь), если не выполнено.

тип возвращаемого значения:
 

boolean (логический)

Пример:

net_box.self:wait_connected()
conn:is_connected()

Проверка активности или закрытия подключения.

возвращается:true (правда) при подключении, false (ложь), если не выполнено.
тип возвращаемого значения:
 boolean (логический)

Пример:

net_box.self:is_connected()
conn:wait_state(state[s][, timeout])

[с 1.7.2] Ожидание нужного состояния.

Параметры:
  • states (string) – необходимое состояние
  • timeout (number) – в секундах
возвращается:

true (правда) при подключении, false (ложь) при окончании времени ожидания или закрытии подключения

тип возвращаемого значения:
 

boolean (логический)

Примеры:

-- бесконечное ожидание состояния 'active':
conn:wait_state('active')

-- ожидание в течение максимум 1,5 секунд:
conn:wait_state('active', 1.5)

-- бесконечное ожидание состояния `active` или `fetch_schema`:
conn:wait_state({active=true, fetch_schema=true})
conn:close()

Закрытие подключения.

Объекты подключения удаляются сборщиком мусора в Lua, как и любой другой Lua-объект, поэтому удалять их явным образом необязательно. Однако, поскольку close() представляет собой системный вызов, лучше всего закрыть соединение явным образом, когда оно больше не используется, с целью ускорения работы сборщика мусора.

Пример:

conn:close()
conn.space.<space-name>:select({field-value, ...} [, {options}])

conn.space.имя-спейса:select({...}) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:select{...}.

Пример:

conn.space.testspace:select({1,'B'}, {timeout=1})

Примечание

Исходя из правил неявной передачи управления, локальный запрос box.space.имя-спейса:select{...} не передает управление, а удаленный conn.space.имя-спейса:select{...} передаст, поэтому глобальные переменные или кортежи в базе данных могут измениться во время удаленного conn.space.имя-спейса:select{...}.

conn.space.<space-name>:get({field-value, ...} [, {options}])

conn.space.имя-спейса:get(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:get(...).

Пример:

conn.space.testspace:get({1})
conn.space.<space-name>:insert({field-value, ...} [, {options}])

conn.space.имя-спейса:insert(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:insert(...).

Пример:

conn.space.testspace:insert({2,3,4,5}, {timeout=1.1})
conn.space.<space-name>:replace({field-value, ...} [, {options}])

conn.space.имя-спейса:replace(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:replace(...).

Пример:

conn.space.testspace:replace({5,6,7,8})
conn.space.<space-name>:update({field-value, ...} [, {options}])

conn.space.имя-спейса:update(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:update(...).

Пример:

conn.space.Q:update({1},{{'=',2,5}}, {timeout=0})
conn.space.<space-name>:upsert({field-value, ...} [, {options}])

conn.space.имя-спейса:upsert(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:upsert(...).

conn.space.<space-name>:delete({field-value, ...} [, {options}])

conn.space.имя-спейса:delete(...) – это удаленный вызов, аналогичный локальному вызову box.space.имя-спейса:delete(...).

conn:eval(Lua-string[, {arguments}[, {options}]])

conn:eval(Lua-строка) оценивает и выполняет выражение в Lua-строке, которое может представлять собой любое выражение или несколько выражений. Требуются права на выполнение; если у пользователя таких прав нет, администратор может их выдать с помощью box.schema.user.grant(имя-пользователя, 'execute', 'universe').

Чтобы гарантировать, что conn:eval вернет то, что возвращает выражение на Lua, начните Lua-строку со слова «return» (вернуть).

Примеры:

tarantool> --Lua-строка
tarantool> conn:eval('function f5() return 5+5 end; return f5();')
---
- 10
...
tarantool> --Lua-строка, {аргументы}
tarantool> conn:eval('return ...', {1,2,{3,'x'}})
---
- 1
- 2
- [3, 'x']
...
tarantool> --Lua-строка, {аргументы}, {парметры}
tarantool> conn:eval('return {nil,5}', {}, {timeout=0.1})
---
- [null, 5]
...
conn:call(function-name[, {arguments}[, {options}]])

conn:call('func', {'1', '2', '3'}) – это удаленный вызов, аналогичный func('1', '2', '3'). Таким образом, conn:call представляет собой удаленный вызов хранимой процедуры. conn:call возвращает то, что возвращает функция.

Ограничение: вызванная функция не может вернуть функцию, например, если func2 определяется как function func2 () return func end, то conn:call(func2) вернет ошибку «error: unsupported Lua type „function“».

Примеры:

tarantool> -- создание 2 функций с conn:eval()
tarantool> conn:eval('function f1() return 5+5 end;')
tarantool> conn:eval('function f2(x,y) return x,y end;')
tarantool> -- вызов первой функции без параметров и опций
tarantool> conn:call('f1')
---
- 10
...
tarantool> -- вызов второй функции с двумя параметрами и одной опцией
tarantool> conn:call('f2',{1,'B'},{timeout=99})
---
- 1
- B
...
conn:timeout(timeout)

timeout(...) – это надстройка, которая определяет время ожидания для запроса. С версии 1.7.4 этот метод объявлен устаревшим – лучше передать значение времени ожидания с помощью параметра {options}.

Пример:

conn:timeout(0.5).space.tester:update({1}, {{'=', 2, 15}})

Хотя timeout(...) объявлен устаревшим, все удаленные вызовы поддерживают его. Использование надстройки обеспечивает совместимость API удаленного соединения с локальным, поэтому отпадает необходимость в отдельном аргументе timeout, который проигнорирует локальная версия. После отправки запроса его нельзя отменить с удаленного сервера даже по истечении времени задержки: окончание времени задержки прерывает только ожидание ответа от удаленного сервера, а не сам запрос.

conn:request(... {is_async=...})

{is_async=true|false} – это опция, которую можно применить во всех запросах net_box, включая conn:call, conn:eval и запросы conn.space.space-name.

По умолчанию, is_async=false, что означает, что запросы будут синхронными для файбера. Файбер блокируется в ожидании ответа на запрос или до истечения времени ожидания. До версии Tarantool’а 1.10 единственным способом выполнения асинхронных запросов было использование отдельных файберов.

is_async=true означает, что запросы будут асинхронными для файбера. Запрос вызывает передачу управления, но файбер не входит в режим ожидания. Сразу же возвращается результат, но это будет не результат запроса, а объект, который может использовать вызывающая программа для получения результат запроса.

У такого сразу же возвращаемого объекта, который мы называем «future» (будущий), есть собственные методы:

  • future:is_ready() вернет true (правда), если доступен результат запроса,
  • future:result() используется для получения результата запроса (возвращает ответ на запрос или nil в случае, если ответ еще не готов или произошла какая-либо ошибка),
  • future:wait_result(timeout) будет ждать, когда результат запроса будет доступен, а затем получит его.
  • future:discard() откажется от объекта.

В обычной ситуации пользователь введет команду future=имя-запроса(...{is_async=true}), а затем либо цикл с проверкой future:is_ready() до тех пор, пока он не вернет true, и получением результата с помощью request_result=future:result(), либо же команду request_result=future:wait_result(...). Возможен вариант, когда клиент проверяет наличие внеполосных сообщений от сервера, вызывая в цикле pairs() – см. box.session.push().

Пример:

tarantool> future = conn.space.tester:insert({900},{is_async=true})
---
...
tarantool> future
---
- method: insert
  response: [900]
  cond: cond
  on_push_ctx: []
  on_push: 'function: builtin#91'
...
tarantool> future:is_ready()
---
- true
...
tarantool> future:result()
---
- [900]
...

Как правило, {is_async=true} используется только при большой загрузке (более 100 000 запросов в секунду) и большой задержке чтения (более 1 секунды), или же при необходимости отправки нескольких одновременных запросов, которые собирают ответы (что иногда называется «отображение-свертка»).

Примечание

Хотя окончательный результат асинхронного запроса не отличается от результата синхронного запроса, у него другая структура: таблица, а не неупакованные значения.

Пример

Ниже приводится пример использования большинства методов net.box.

Данный пример сработает на конфигурации из песочницы, предполагается, что:

  • экземпляр Tarantool’а запущен на localhost 127.0.0.1:3301,
  • создан спейс под названием tester с первичным числовым ключом и кортежем, в котором есть ключ со значением= 800,
  • у текущего пользователя есть права на чтение, запись и выполнение.

Ниже приведены команды для быстрой настройки песочницы:

box.cfg{listen = 3301}
s = box.schema.space.create('tester')
s:create_index('primary', {type = 'hash', parts = {1, 'unsigned'}})
t = s:insert({800, 'TEST'})
box.schema.user.grant('guest', 'read,write,execute', 'universe')

А здесь приведен пример:

tarantool> net_box = require('net.box')
---
...
tarantool> function example()
         >   local conn, wtuple
         >   if net_box.self:ping() then
         >     table.insert(ta, 'self:ping() succeeded')
         >     table.insert(ta, '  (no surprise -- self connection is pre-established)')
         >   end
         >   if box.cfg.listen == '3301' then
         >     table.insert(ta,'The local server listen address = 3301')
         >   else
         >     table.insert(ta, 'The local server listen address is not 3301')
         >     table.insert(ta, '(  (maybe box.cfg{...listen="3301"...} was not stated)')
         >     table.insert(ta, '(  (so connect will fail)')
         >   end
         >   conn = net_box.connect('127.0.0.1:3301')
         >   conn.space.tester:delete({800})
         >   table.insert(ta, 'conn delete done on tester.')
         >   conn.space.tester:insert({800, 'data'})
         >   table.insert(ta, 'conn insert done on tester, index 0')
         >   table.insert(ta, '  primary key value = 800.')
         >   wtuple = conn.space.tester:select({800})
         >   table.insert(ta, 'conn select done on tester, index 0')
         >   table.insert(ta, '  number of fields = ' .. #wtuple)
         >   conn.space.tester:delete({800})
         >   table.insert(ta, 'conn delete done on tester')
         >   conn.space.tester:replace({800, 'New data', 'Extra data'})
         >   table.insert(ta, 'conn:replace done on tester')
         >   conn.space.tester:update({800}, {{'=', 2, 'Fld#1'}})
         >   table.insert(ta, 'conn update done on tester')
         >   conn:close()
         >   table.insert(ta, 'conn close done')
         > end
---
...
tarantool> ta = {}
---
...
tarantool> example()
---
...
tarantool> ta
---
- - self:ping() succeeded
  - '  (no surprise -- self connection is pre-established)'
  - The local server listen address = 3301
  - conn delete done on tester.
  - conn insert done on tester, index 0
  - '  primary key value = 800.'
  - conn select done on tester, index 0
  - '  number of fields = 1'
  - conn delete done on tester
  - conn:replace done on tester
  - conn update done on tester
  - conn close done
...