Saltar a contenido

tarantool: Biblioteca para trabajar con tarantool desde nginx con el módulo Lua embebido o con nginx-module-lua

Instalación

Si no has configurado la suscripción al repositorio RPM, regístrate. Luego puedes proceder con los siguientes pasos.

CentOS/RHEL 7 o 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

Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.

Este documento describe lua-resty-tarantool v0.3 lanzado el 21 de octubre de 2015.


Introducción

Esta es una biblioteca para conectarse a la base de datos NoSQL tarantool. Esta base de datos tiene características muy interesantes que la convierten en una especie de puente entre una base de datos tradicional basada en SQL y almacenes orientados a documentos como CouchDB.

Es un fork de otro proyecto con el que no estaba satisfecho. Está abundantemente documentado y se actualiza respecto a la API de tarantool. Notablemente con soporte para el comando upsert.

Otra cosa a tener en cuenta es que la biblioteca intenta ser consistente entre la forma en que se emiten los comandos update y upsert en la consola usando Lua y la forma en que funciona la API. Notablemente los números de campo. En la consola, un número de campo tiene en cuenta la existencia de un índice primario como el primer campo. Por lo tanto, cualquier campo que venga después tendrá una posición que lo considere. Específicamente al especificar los operadores a utilizar para las operaciones de update o upsert.

Uso

Creando una conexión

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,
})
Lo anterior crea un objeto de conexión que se conecta a una instancia del servidor tarantool que se ejecuta en el loopback en el puerto 3301, para el usuario luser con la contraseña some password. Consulta el manual de Tarantool sobre autenticación para obtener detalles sobre cómo configurar usuarios y asignarles privilegios.

El tiempo de espera del socket (recepción y envío) es de 2 segundos (2000 ms).

set_timeout

settimeout(<connection object>, <timeout in ms>)

Establece tanto los tiempos de espera de envío como de recepción en milisegundos para un socket dado.

tnt:set_timeout(5000) -- 5s timeout para operaciones de envío/recepción

La función devuelve true si la configuración tiene éxito, nil si no. Ten en cuenta que para que el tiempo de espera tenga efecto, esta función debe ser invocada antes de que se establezca la conexión, es decir, antes de invocar la función connect. Alternativamente, el tiempo de espera puede especificarse al crear el objeto de conexión (cosocket).

connect

connect(<connection object>)

Conecta el socket creado anteriormente al puerto y dirección especificados al crear el objeto de conexión.

tar:connect()
La función devuelve true si la conexión tiene éxito, nil si no.

set_keepalive

set_keepalive(<connection object>)

Hace que la conexión creada se envíe a un grupo de conexiones para que la conexión se mantenga activa a través de múltiples solicitudes.

tar:set_keepalive()

La función devuelve true si el socket se envía correctamente al grupo de conexiones (mantener viva la conexión). nil si no.

disconnect

disconnect(<connection object>)

Cierra una conexión a un servidor tarantool dado que se ejecuta en una dirección y puerto específicos.

tar:disconnect()

La función devuelve true si la conexión se cierra correctamente. nil si no.

ping

El comando ping es útil para monitorear el servidor tarantool y ver si está disponible. Si está disponible para consultas, devuelve la cadena PONG.

tar:ping()
-- devuelve PONG

select

La operación select consulta una base de datos (espacio) dada para recuperar registros.

select(<connection object>, <space name>, <index>, <key>, <options>)

donde <options> es un argumento opcional que puede consistir en una tabla que puede tener las siguientes claves:

  • offset: número de registros a omitir al realizar la consulta.
  • limit: el número máximo de registros a devolver.
  • iterator: un número que especifica el iterador a utilizar. Especificado por la tabla:

local iterator_keys = {
  EQ = 0, -- igualdad
  REQ = 1, -- igualdad inversa
  ALL = 2, -- todas las tuplas en un índice
  LT = 3, -- menor que
  LE = 4, -- menor o igual
  GE = 5, -- mayor o igual
  GT = 6, -- mayor que
  BITSET_ALL_SET = 7, -- bits en la máscara de bits todos establecidos
  BITSET_ANY_SET = 8, -- cualquiera de los bits en la máscara de bits están establecidos
  BITSET_ALL_NOT_SET = 9, -- ninguno de los bits en la máscara de bits están establecidos
}
Más detalles sobre iteradores en el manual de tarantool.

ejemplos de select

Consultar el espacio _space (DB) para obtener el id del espacio _index.

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

-- respuesta:
[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"}]]]
La solicitud anterior es equivalente a la solicitud de consola:

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

Consultar el espacio 'activities' para las actividades con un price menor a 300

-- N.B. price es un índice del espacio activities.
local res, err = tar:select('activities', 'price', 300, { iterator = 'LT' })
La solicitud anterior es equivalente a la solicitud de consola:

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

insert

insert(<connection object>, <space name>, <tuple>)

donde <tuple> es la tupla a insertar en <space> mientras se establece el índice primario, que es único, al valor especificado en la tupla.

La función devuelve el registro insertado si la operación tiene éxito.

ejemplos de insert

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

-- respuesta: 
[[16,120,{"activity":"surf","price":121}]]
La solicitud anterior es equivalente a la solicitud de consola:

box.space.activities:insert({16, 120, { activity = 'surf', price = 121 }})
16 es el valor del índice primario aquí. Esto significa que para un índice de tipo entero, este será el registro con el índice primario 16.

replace

replace(<connection object>, <space name>, <tuple>)

El comando replace es similar en la invocación y firma al comando insert. Pero ahora estamos buscando reemplazar un registro que ya existe en lugar de insertar uno nuevo. Necesitamos nuevamente el valor de un índice primario único. Pero ahora el valor debe existir para que la operación tenga éxito. Si la operación tiene éxito, se devuelve el registro con los valores reemplazados.

ejemplos de replace

local res, err = tar:replace('activities', { 16, 120, { activity = 'surf', price = 120 } })
-- respuesta:
[[16,120,{"activity":"surf","price":120}]]
Aquí reemplazamos el precio anterior de 121 por 120. El valor del índice primario, 16, coincide con el registro que insertamos anteriormente.

La solicitud anterior es equivalente a la solicitud de consola:

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

update

update(<connection object>, <space name>, <index>, <key>, <operator list>)

donde <operator list> es la lista de operadores como se especifica en el manual de tarantool. El par (, ) identifica de manera única un registro, es decir, el <key> es un valor del índice primario (único) <index>.

<operator list> es una tabla de la forma:

{ <operator>, <field position>, <value> }
los operadores son:

  • + para sumar a un campo numérico.
  • - para restar a un campo numérico.
  • & para operación AND a nivel de bits entre dos enteros sin signo.
  • | para operación OR a nivel de bits entre dos enteros sin signo.
  • ^ para operación XOR a nivel de bits entre dos enteros sin signo.
  • : para concatenación de cadenas.
  • ! para inserción de campo.
  • # para eliminación de campo.
  • = para asignar un valor dado a un campo.

devuelve el registro actualizado si la operación es exitosa.

ejemplos de update

local res, err = tar:update('activities', 'primary', 16, { { '=', 2, 341 }, { '=', 3,  { activity = 'kitesurfing', price = 341 }}} )
-- respuesta:
[16,341,{"activity":"kitesurfing","price":341}]]
El registro con el índice primary 16 que insertamos anteriormente fue actualizado.

La solicitud anterior es equivalente a la solicitud de consola:

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

upsert

upsert(<connection object>, <space name>, <key>, <operator list>, <new tuple>)

aparte del argumento <new tuple>, la firma de la función es similar a update. De hecho, upsert es dos comandos en uno. Actualiza si el registro especificado por el par (, ) existe e inserta si no. La clave es un valor de un índice primario, es decir, es único. La <new tuple> es la tupla que se insertará si el valor <key> no existe en el <index>. Devuelve una tabla vacía {} si la operación tiene éxito. Si la operación no tiene éxito, devuelve nil.

ejemplos de upsert

Un insert.

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 17, 450, { activity = 'waterski', price = 365 }})
-- respuesta:
{}
Hemos insertado un nuevo registro con clave 17 para el índice primario de la tupla:

{ 18, 450, { activity = 'waterski', price = 365 }}
La solicitud anterior es equivalente a la solicitud de consola:

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

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 18, 285, { activity = 'kitesurfing', price = 285 }})
-- respuesta:
{}
Ahora realizamos una actualización del registro identificado por la clave 17 en el índice primary (único).

delete

delete(<connection object>, <space>, <key>)

elimina el registro especificado de manera única por <key> del <space>. Ten en cuenta que <key> debe pertenecer a un índice primario (único). Devuelve el registro eliminado si la operación tiene éxito.

ejemplos de delete

local response, err = tar:delete('activities', 17)
-- respuesta:
[17,450,{"activity":"waterski","price":365}]]
Eliminamos el registro identificado de manera única por la clave 17 en el índice primario del espacio activities.

La solicitud anterior es equivalente a la solicitud de consola:

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

call

call(<connection object>, <proc>, <args>)

Invoca un procedimiento almacenado (función Lua) en el servidor tarantool al que estamos conectados. Devuelve los resultados de la invocación.

ejemplos de call

Dado que la consola de tarantool es un REPL de Lua, cualquier función puede ser invocada siempre que esté disponible en el entorno.

local res, err = tar:call('table.concat', { {'hello', ' ', 'world' } })
-- respuesta:
[["hello world"]]
Llamamos a la función table.concat de la biblioteca de tablas para concatenar la tabla:

{'hello', ' ', 'world' }
La solicitud anterior es equivalente a la solicitud de consola:

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

Para muchos ejemplos de procedimientos almacenados de tarantool, consulta el repositorio; https://github.com/mailru/tarlua

hide_version_header

hide_version_header(<connection object>)

Por defecto, cada respuesta envía un encabezado HTTP personalizado X-Tarantool-Version con la versión del servidor tarantool.

X-Tarantool-Version: 1.6.6-191-g82d1bc3

Invocar hide_version_header elimina el encabezado.

tar:hide_version_header()

No devuelve valores.

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-tarantool.