Версия:

Вложенный модуль box.session

Вложенный модуль box.session

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

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

Сессия – это объект, связанный с каждым подключением клиента.

Индекс

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

Имя Использование
box.session.id() Получение идентификатора текущей сессии
box.session.exists() Проверка наличия сессии
box.session.peer() Получение адреса хоста и порта подключенного узла
box.session.sync() Получение целочисленной константы sync
box.session.user() Получение имени текущего пользователя
box.session.type() Получение типа соединения или повода к действию
box.session.su() Изменение текущего пользователя
box.session.uid() Получение идентификатора текущего пользователя
box.session.euid() Получение идентификатора текущего действующего пользователя
box.session.storage Таблица с именами и значениями по сессии
box.session.on_connect() Определение триггера для подключения
box.session.on_disconnect() Определение триггера для отключения
box.session.on_auth() Определение триггера для аутентификации
box.session.push() Отправка внеполосного сообщения
box.session.id()
возвращается:уникальный идентификатор (ID) для текущей сессии. Результатом может быть 0 или -1, что означает, что сессии нет.
тип возвращаемого значения:
 число
box.session.exists(id)
возвращается:1, если сессия есть; 0, если сессии нет.
тип возвращаемого значения:
 число
box.session.peer(id)

Данная функция сработает только в том случае, если есть подключенная программа, то есть если было выполнено подключение к отдельному экземпляру Tarantool’а.

возвращается:Адрес хоста и порт подключенного узла, например «127.0.0.1:55457». Если существует сессия, но отсутствует подключение к отдельному экземпляру, вернется null. Команда выполняется на экземпляре сервера, поэтому «локальное имя» – это хост и порт экземпляра сервера, а «имя узла» – это хост и порт клиента.
тип возвращаемого значения:
 string (строка)

Возможные ошибки: „session.peer(): сессия отсутствует“

box.session.sync()
возвращается:the value of the sync integer constant used in the binary protocol. This value becomes invalid when the session is disconnected.
тип возвращаемого значения:
 число

This function is local for the request, i.e. not global for the session. If the connection behind the session is multiplexed, this function can be safely used inside the request processor.

box.session.user()
возвращается:имя текущего пользователя
тип возвращаемого значения:
 string (строка)
box.session.type()
возвращается:тип соединения или повод к действию.
тип возвращаемого значения:
 string (строка)

Возможные возвращаемые значения:

  • „binary“ (бинарное), если подключение было выполнено по бинарному протоколу, например, к объекту с помощью box.cfg{listen=…};
  • „console“ (консоль), если подключение было выполнено по административной консоли, например, к объекту с помощью console.listen;
  • „repl“ (репликация), если подключение было выполнено напрямую, например, при использовании Tarantool’а в качестве клиента;
  • „applier“ (наложение), если действие происходит по причине репликации, независимо от типа подключения;
  • „background“ (в фоне), если действие происходит в фоновом файбере, независимо от того, был ли Tarantool запущен в фоновом режиме.

box.session.type() используется для триггера при замене on_replace() на реплике – значение будет „applier“ только в том случае, если триггер был активирован по причине запроса, выполненного на мастере.

box.session.su(user-name[, function-to-execute])

Изменение текущего пользователя Tarantool’а – аналогично Unix-команде su.

Или, если указана выполняемая функция (function-to-execute), временное изменение текущего пользователя Tarantool’а во время выполнения функции – аналогично Unix-команде sudo.

Параметры:
  • user-name (string) – целевое имя пользователя
  • function-to-execute – имя функции или определение функции. Дополнительные параметры могут передаваться в box.session.su, они будут интерпретироваться как параметры выполняемой функции.

Пример

tarantool> function f(a) return box.session.user() .. a end
---
...

tarantool> box.session.su('guest', f, '-xxx')
---
- guest-xxx
...

tarantool> box.session.su('guest',function(...) return ... end,1,2)
---
- 1
- 2
...
box.session.uid()
возвращается:ID текущего пользователя.
тип возвращаемого значения:
 число

У каждого пользователя есть уникальное имя (узнать с помощью box.session.user()) и уникальный идентификатор (узнать с помощью box.session.uid()). Значения хранятся вместе в спейсе _user.

box.session.euid()
возвращается:рабочий ID текущего пользователя.

Аналогично box.session.uid(), за исключением двух случаев:

  • Первый случай: если вызов box.session.euid() выполняется в рамках функции, вызываемой по box.session.su(user-name, function-to-execute) – в таком случае box.session.euid() вернет измененный идентификатор пользователя (пользователь, который указан в параметре user-name функции su), но box.session.uid() вернет идентификатор оригинального пользователя (пользователя, который вызывает функцию su).
  • Второй случай: если вызов box.session.euid() выполняется в рамках функции по box.schema.func.create(function-name, {setuid= true}), и используется бинарный протокол – в таком случае box.session.euid() вернет идентификатор пользователя, который создал функцию «function-name», а box.session.uid() вернет идентификатор пользователя, который вызывает эту функцию «function-name».
тип возвращаемого значения:
 число

Пример

tarantool> box.session.su('admin')
---
...
tarantool> box.session.uid(), box.session.euid()
---
- 1
- 1
...
tarantool> function f() return {box.session.uid(),box.session.euid()} end
---
...
tarantool> box.session.su('guest', f)
---
- - 1
  - 0
...
box.session.storage

Lua-таблица с произвольными неупорядоченными именами и значениями по сессии, которая хранится до конца сессии. Например, эту таблицу можно использовать для хранения текущих задач при работе с очередями сообщений в Tarantool’е.

Пример

tarantool> box.session.peer(box.session.id())
---
- 127.0.0.1:45129
...
tarantool> box.session.storage.random_memorandum = "Don't forget the eggs"
---
...
tarantool> box.session.storage.radius_of_mars = 3396
---
...
tarantool> m = ''
---
...
tarantool> for k, v in pairs(box.session.storage) do
         >   m = m .. k .. '='.. v .. ' '
         > end
---
...
tarantool> m
---
- 'radius_of_mars=3396 random_memorandum=Don''t forget the eggs. '
...
box.session.on_connect([trigger-function[, old-trigger-function]])

Определение исполняемого триггера во время создания новой сессии при подключению по консоли console.connect. Функция с триггером будет первой исполняемой функцией после создания сессии. Если триггер не выполняется и выдает ошибку, эта ошибка отправляется на клиент, и подключение разрывается.

Параметры:
  • trigger-function (function) – функция, в которой будет триггер
  • old-trigger-function (function) – существующая функция с триггером, которую заменит новая
возвращается:

nil или указатель функции

Если указаны параметры (nil, old-trigger-function), старый триггер будет удален.

Если не указан ни один параметр, ответом будет список существующих функций с триггером.

Подробная информация о характеристиках триггера находится в разделе Триггеры.

Пример

tarantool> function f ()
         >   x = x + 1
         > end
tarantool> box.session.on_connect(f)

Предупреждение

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

box.session.on_disconnect([trigger-function[, old-trigger-function]])

Определение исполняемого триггера после отключения клиента. Если функция с триггером вызывает ошибку, то ошибка записывается в журнал, в противном случае записей не будет. Триггер вызывается во время сессии клиента и может получить доступ к свойствам сессии, как box.session.id().

Начиная с версии 1.10, функция с триггером вызывается сразу же после прерывания сессии, даже если сделанные запросы не были выполнены.

Параметры:
  • trigger-function (function) – функция, в которой будет триггер
  • old-trigger-function (function) – существующая функция с триггером, которую заменит новая
возвращается:

nil или указатель функции

Если указаны параметры (nil, old-trigger-function), старый триггер будет удален.

Если не указан ни один параметр, ответом будет список существующих функций с триггером.

Подробная информация о характеристиках триггера находится в разделе Триггеры.

Пример №1

tarantool> function f ()
         >   x = x + 1
         > end
tarantool> box.session.on_disconnect(f)

Пример №2

После следующей серии запросов экземпляр Tarantool’а запишет сообщение с помощью модуля log при подключении или отключении любого пользователя.

function log_connect ()
  local log = require('log')
  local m = 'Connection. user=' .. box.session.user() .. ' id=' .. box.session.id()
  log.info(m)
end

function log_disconnect ()
  local log = require('log')
  local m = 'Disconnection. user=' .. box.session.user() .. ' id=' .. box.session.id()
  log.info(m)
end

box.session.on_connect(log_connect)
box.session.on_disconnect(log_disconnect)

Вот что может быть записано в файл журнала при обычной установке:

2014-12-15 13:21:34.444 [11360] main/103/iproto I>
    Connection. user=guest id=3
2014-12-15 13:22:19.289 [11360] main/103/iproto I>
    Disconnection. user=guest id=3
box.session.on_auth([trigger-function[, old-trigger-function]])

Определение триггера, используемого во время аутентификации.

Вызов функции on_auth с триггером происходит в следующих обстоятельствах:

  1. Функция console.connect включает в себя проверку аутентификации всех пользователей, кроме „guest“. Вызов функции on_auth с триггером происходит после триггера on_connect только в том случае, если подключение было успешным.
  2. В бинарном протоколе есть отдельный пакет для аутентификации. В этом случае подключение и аутентификация считаются отдельными действиям.

В отличие от других типов триггеров, вызов функций с триггером on_auth происходит до события. Таким образом, функция с таким триггером, как function auth_function () v = box.session.user(); end, определит v как «guest», то есть имя пользователя до проведения аутентификации. Чтобы получить имя пользователя после проведения аутентификации, используйте специальный синтаксис: function auth_function (user_name) v = user_name; end

Если триггер не выполняется и выдает ошибку, эта ошибка отправляется на клиент, и подключение разрывается.

Параметры:
  • trigger-function (function) – функция, в которой будет триггер
  • old-trigger-function (function) – существующая функция с триггером, которую заменит новая
возвращается:

nil или указатель функции

Если указаны параметры (nil, old-trigger-function), старый триггер будет удален.

Если не указан ни один параметр, ответом будет список существующих функций с триггером.

Подробная информация о характеристиках триггера находится в разделе Триггеры.

Пример 1

tarantool> function f ()
         >   x = x + 1
         > end
tarantool> box.session.on_auth(f)

Пример 2

Более сложный пример с двумя экземплярами сервера.

Первый экземпляр сервера настроен на прослушивание по порту 3301; имя пользователя по умолчанию – „admin“. Есть три триггера on_auth:

  • В первом триггере есть функция без аргументов, которая только смотрит на box.session.user().
  • Во втором триггере есть функция с аргументом user_name, которая может смотреть на box.session.user() и user_name.
  • В третьем триггере есть функция с аргументом user_name и аргументом status, которая может смотреть на box.session.user() и user_name, и``status``.

Второй экземпляр сервера подключится по console.connect, а затем отобразит переменные, определенные функциями с триггером.

-- На первом экземпляре сервера, прослушивание на котором настроено на порт 3301
box.cfg{listen=3301}
function function1()
  print('function 1, box.session.user()='..box.session.user())
  end
function function2(user_name)
  print('function 2, box.session.user()='..box.session.user())
  print('function 2, user_name='..user_name)
  end
function function3(user_name, status)
  print('function 3, box.session.user()='..box.session.user())
  print('function 3, user_name='..user_name)
  if status == true then
    print('function 3, status = true, authorization succeeded')
    end
  end
box.session.on_auth(function1)
box.session.on_auth(function2)
box.session.on_auth(function3)
box.schema.user.passwd('admin')
-- На втором экземпляре сервера, который подключается по порту 3301
console = require('console')
console.connect('admin:admin@localhost:3301')

Теперь результат выглядит следующим образом:

function 3, box.session.user()=guest
function 3, user_name=admin
function 3, status = true, authorization succeeded
function 2, box.session.user()=guest
function 2, user_name=admin
function 1, box.session.user()=guest
box.session.push(message[, sync])

Создание внеполосного сообщения. Под внеполосным мы понимаем дополнительное сообщение, которое дополняет то, что отправляется в сети по обычным каналам. Хотя box.session. push() можно вызвать в любое время, на практике эта функция используется в сетях, настроенных с помощью модуля net.box, и вызывается сервером (на «удаленной системе с базой данных», если использовать нашу терминологию для net.box), а у клиента есть возможность принимать такие сообщения.

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

Параметры:
  • message (any-Lua-type) – что отправляется
  • sync (int) – необязательный аргумент, который показывает информацию о сессии, полученную из предшествующего вызова box_session:sync(). Если не указать, по умолчанию используется текущее значение box.session.sync().
тип возвращаемого значения:
 

{nil, ошибка} или true:

  • Если результатом будет ошибка, то вернется nil вместе с объектом ошибки.
  • Если результатом будет не ошибка, то вернется логическое значение true (правда).
  • Если возвращается true, сообщение отправлено в буфер сети в виде пакета с кодом IPROTO_CHUNK (0x80).

Единственная задача сервера – вызвать box.session.push(), поскольку нет автоматического механизма, который показал бы, что сообщение получено.

Задача клиента заключается в том, чтобы проверять наличие таких сообщений после отправки чего-либо на сервер. Основные клиентские методы – conn:call, conn:eval, conn:select, conn:insert, conn:replace, conn:update, conn:upsert, delete – могут привести к отправке такого сообщения сервером.

Ситуация 1: когда клиент делает синхронный вызов со значением параметра {async=false} по умолчанию. Есть два необязательных дополнительных параметра: on_push=function-name и on_push_ctx=function-argument. Когда клиент получает внеполосное сообщение в сессии, он вызывает «имя-функции(аргумент-функции)». Например, с такими значениями параметров: {on_push=table.insert, on_push_ctx=messages} – клиент произведет вставку полученных данных в таблицу под названием „messages“.

Ситуация 2: когда клиент делает асинхронный вызов с измененным значением параметра {async=true}. Здесь не разрешены on_push и on_push_ctx, но сообщения можно увидеть путем вызова pairs() в цикле.

Осложненная ситуация 2: pairs() зависит от времени ожидания. Таким образом, есть необязательный аргумент – время ожидания для итерации. Если время ожидания истечет до получения нового сообщения или окончательного ответа, вернется ошибка. Чтобы проверить наличие ошибки, можно использовать первый параметр в цикле (если цикл начинается с «for i, message in future:pairs()», то первым параметром в цикле будет i). Если это будет box.NULL, то второй параметр (в нашем примере «message») – это объект ошибки.

Пример

-- Создайте две оболочки. В оболочке №1 настройте сервер, а
-- в нем функцию, которая содержит box.session.push:
box.cfg{listen=3301}
box.schema.user.grant('guest','read,write,execute','universe')
x = 0
fiber = require('fiber')
function server_function() x=x+1; fiber.sleep(1); box.session.push(x); end

-- В оболочке №2 подключитесь к серверу в качестве клиента, который
-- поддерживает Lua (как второй Tarantool-сервер, работающий
-- в качестве клиента), и создайте таблицу, в которую мы будем получать сообщения:
net_box = require('net.box')
conn = net_box.connect(3301)
messages_from_server = {}

-- В оболочке №2 удаленно вызовите функцию и получите
-- СИНХРОННОЕ внеполосное сообщение:
conn:call('server_function', {},
          {is_async = false,
           on_push = table.insert,
           on_push_ctx = messages_from_server})
messages_from_server
-- Через секунду, во время которой происходит запрос fiber.sleep()
-- в server_function, результат в таблице
--  messages_from_server будет следующим: 1. Проверим:
-- tarantool> messages_from_server
-- ---
-- - - 1
-- ...
-- Хорошо. Это означает, что box.session.push(x) сработала,
-- поскольку мы знаем, что x был 1.

-- В оболочке №2 удаленно вызовите ту же самую функцию
-- для получения АСИНХРОННОГО внеполосного сообщения. При этом мы не можем
-- использовать параметры on_push и on_push_ctx, но можем использовать pairs():
future = conn:call('server_function', {}, {is_async = true})
messages = {}
keys = {}
for i, message in future:pairs() do
    table.insert(messages, message) table.insert(keys, i) end
messages
future:wait_result(1000)
for i, message in future:pairs() do
    table.insert(messages, message) table.insert(keys, i) end
messages
-- Задержки нет, поскольку conn:call не ждет
-- окончания вызова функции server_function. После первой итерации
-- цикла pairs(), видим, что таблица пуста. Это выглядит так:
-- tarantool> messages
-- ---
-- - - 2
--   - []
-- ...
-- Это нормально, поскольку сервер еще не вызвал
-- box.session.push(). При второй итерации
-- цикла pairs(), видим значение x во время
-- второго вызова box.session.push(). Так:
-- tarantool> messages
-- ---
-- - - 2
--   - &0 []
--   - 2
--   - *0
-- ...
-- Хорошо. Это означает, что сообщение было асинхронным, и
-- box.session.push() выполнила свою задачу.