Pular para conteúdo

tarantool: Biblioteca para trabalhar com tarantool a partir do nginx com o módulo Lua embutido ou com nginx-module-lua

Instalação

Se você ainda não configurou a assinatura do repositório RPM, inscreva-se. Então você pode prosseguir com os seguintes passos.

CentOS/RHEL 7 ou 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 com o NGINX, certifique-se de que o nginx-module-lua esteja instalado.

Este documento descreve lua-resty-tarantool v0.3 lançado em 21 de outubro de 2015.


Introdução

Esta é uma biblioteca para conectar ao banco de dados NoSQL tarantool. Este banco de dados possui características muito interessantes que o tornam uma espécie de ponte entre um banco de dados tradicional baseado em SQL e armazenamentos orientados a documentos como o CouchDB.

É um fork de outro projeto com o qual eu não estava satisfeito. É amplamente documentado e está atualizado em relação à API do tarantool. Notavelmente com suporte para o comando upsert.

Outra coisa a ter em mente é que a biblioteca tenta ser consistente entre a forma como os comandos update e upsert são emitidos no console usando Lua e a forma como a API funciona. Notavelmente os números dos campos. No console, um número de campo leva em conta a existência de um índice primário como o primeiro campo. Portanto, qualquer campo que venha depois terá uma posição que considera isso. Especificamente ao especificar os operadores a serem usados para as operações update ou upsert.

Uso

Criando uma conexão

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,
})
O acima cria um objeto de conexão que se conecta a uma instância do servidor tarantool rodando no loopback na porta 3301, para o usuário luser com a senha some password. Veja o manual do Tarantool sobre autenticação para detalhes sobre como configurar usuários e atribuir privilégios a eles.

O tempo limite do socket (receber e enviar) é de 2 segundos (2000 ms).

set_timeout

settimeout(<objeto de conexão>, <tempo limite em ms>)

Define tanto os tempos limites de envio quanto de recebimento em milissegundos para um dado socket.

tnt:set_timeout(5000) -- tempo limite de 5s para operações de envio/recebimento

A função retorna verdadeiro se a configuração for bem-sucedida, nil se não. Note que para o tempo limite ter efeito, esta função precisa ser invocada antes da conexão ser estabelecida, ou seja, antes de invocar a função connect. Alternativamente, o tempo limite pode ser especificado ao criar o objeto de conexão (cosocket).

connect

connect(<objeto de conexão>)

Conecta o socket criado acima à porta e ao endereço especificados ao criar o objeto de conexão.

tar:connect()
A função retorna verdadeiro se a conexão for bem-sucedida, nil se não.

set_keepalive

set_keepalive(<objeto de conexão>)

Faz com que a conexão criada seja enviada para um pool de conexões, de modo que a conexão seja mantida ativa em várias solicitações.

tar:set_keepalive()

A função retorna verdadeiro se o socket for enviado com sucesso para o pool de conexões (manter viva). nil se não.

disconnect

disconnect(<objeto de conexão>)

Fecha uma conexão com um determinado servidor tarantool rodando em um determinado endereço e porta.

tar:disconnect()

A função retorna verdadeiro se a conexão for fechada com sucesso. nil se não.

ping

O comando ping é útil para monitorar o servidor tarantool para ver se ele está disponível. Se estiver disponível para consultas, retorna a string PONG.

tar:ping()
-- retorna PONG

select

A operação select consulta um determinado banco de dados (espaço) para recuperar registros.

select(<objeto de conexão>, <nome do espaço>, <índice>, <chave>, <opções>)

onde <opções> é um argumento opcional que pode consistir em uma tabela que pode ter as seguintes chaves:

  • offset: número de registros a serem ignorados ao fazer a consulta.
  • limit: o número máximo de registros a serem retornados.
  • iterator: um número especificando o iterador a ser usado. Especificado pela tabela:

local iterator_keys = {
  EQ = 0, -- igualdade
  REQ = 1, -- igualdade reversa
  ALL = 2, -- todas as tuplas em um índice
  LT = 3, -- menor que
  LE = 4, -- menor ou igual
  GE = 5, -- maior ou igual
  GT = 6, -- maior que
  BITSET_ALL_SET = 7, -- bits na máscara de bits todos definidos
  BITSET_ANY_SET = 8, -- qualquer um dos bits na máscara de bits está definido
  BITSET_ALL_NOT_SET = 9, -- nenhum dos bits na máscara de bits está definido
}
Mais detalhes sobre iteradores no manual do tarantool.

exemplos de select

Consultar o espaço _space (DB) para obter o id do espaço _index.

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

-- resposta:
[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"}]]]
A solicitação acima é equivalente à solicitação do console:

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

Consultar o espaço 'activities' para as atividades com um price menor que 300

-- N.B. price é um índice do espaço activities.
local res, err = tar:select('activities', 'price', 300, { iterator = 'LT' })
A solicitação acima é equivalente à solicitação do console:

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

insert

insert(<objeto de conexão>, <nome do espaço>, <tupla>)

onde <tupla> é a tupla a ser inserida no <espaço> enquanto define o índice primário, que é único, para o valor especificado na tupla.

A função retorna o registro inserido se a operação for bem-sucedida.

exemplos de insert

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

-- resposta: 
[[16,120,{"activity":"surf","price":121}]]
A solicitação acima é equivalente à solicitação do console:

box.space.activities:insert({16, 120, { activity = 'surf', price = 121 }})
16 é o valor do índice primário aqui. Isso significa que para um índice do tipo inteiro, este será o registro com o índice primário 16.

replace

replace(<objeto de conexão>, <nome do espaço>, <tupla>)

O comando replace é semelhante na invocação e assinatura ao comando insert. Mas agora estamos procurando substituir um registro que já existe em vez de inserir um novo. Precisamos novamente do valor de um índice primário único. Mas agora o valor deve existir para que a operação seja bem-sucedida. Se a operação for bem-sucedida, o registro com os valores substituídos é retornado.

exemplos de replace

local res, err = tar:replace('activities', { 16, 120, { activity = 'surf', price = 120 } })
-- resposta:
[[16,120,{"activity":"surf","price":120}]]
Aqui substituímos o preço anterior de 121 por 120. O valor do índice primário, 16, corresponde ao registro que inserimos acima.

A solicitação acima é equivalente à solicitação do console:

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

update

update(<objeto de conexão>, <nome do espaço>, <índice>, <chave>, <lista de operadores>)

onde <lista de operadores> é a lista de operadores conforme especificado no manual do tarantool. O par (<índice>, ) identifica de forma única um registro, ou seja, a <chave> é um valor do índice primário (único) <índice>.

<lista de operadores> é uma tabela da forma:

{ <operador>, <posição do campo>, <valor> }
os operadores são:

  • + para adicionar a um campo numérico.
  • - para subtrair de um campo numérico.
  • & para operação AND bit a bit entre dois inteiros sem sinal.
  • | para operação OR bit a bit entre dois inteiros sem sinal.
  • ^ para operação XOR bit a bit entre dois inteiros sem sinal.
  • : para concatenação de strings.
  • ! para inserção de campo.
  • # para exclusão de campo.
  • = para atribuir um valor dado a um campo.

retorna o registro atualizado se a operação for bem-sucedida.

exemplos de update

local res, err = tar:update('activities', 'primary', 16, { { '=', 2, 341 }, { '=', 3,  { activity = 'kitesurfing', price = 341 }} } )
-- resposta:
[16,341,{"activity":"kitesurfing","price":341}]]
O registro com índice primary 16 que inserimos acima foi atualizado.

A solicitação acima é equivalente à solicitação do console:

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

upsert

upsert(<objeto de conexão>, <nome do espaço>, <chave>, <lista de operadores>, <nova tupla>)

além do argumento <nova tupla>, a assinatura da função é semelhante à de update. Na verdade, upsert é dois comandos em um. update se o registro especificado pelo par (<índice>, ) existir e insert se não. A chave é um valor de um índice primário, ou seja, é único. A <nova tupla> é a tupla a ser inserida se o valor da <chave> não existir no <índice>. Retorna uma tabela vazia {} se a operação for bem-sucedida. Se a operação não for bem-sucedida, retorna nil.

exemplos de upsert

Um insert.

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 17, 450, { activity = 'waterski', price = 365 }})
-- resposta:
{}
Nós inserimos um novo registro com a chave 17 para o índice primário da tupla:

{ 18, 450, { activity = 'waterski', price = 365 }}
A solicitação acima é equivalente à solicitação do console:

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

local res, err = tar:upsert('activities', 17, { { '=', 2, 450 }, { '=', 3,  { activity = 'submarine tour 8', price = 450 }}}, { 18, 285, { activity = 'kitesurfing', price = 285 }})
-- resposta:
{}
Agora realizamos uma atualização do registro identificado pela chave 17 no índice primary (único).

delete

delete(<objeto de conexão>, <espaço>, <chave>)

deleta o registro especificado de forma única pela <chave> do <espaço>. Note que <chave> deve pertencer a um índice primário (único). Retorna o registro deletado se a operação for bem-sucedida.

exemplos de delete

local response, err = tar:delete('activities', 17)
-- resposta:
[17,450,{"activity":"waterski","price":365}]]
Deletamos o registro identificado de forma única pela chave 17 no índice primário do espaço activities.

A solicitação acima é equivalente à solicitação do console:

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

call

call(<objeto de conexão>, <proc>, <args>)

Invoca uma procedimento armazenado (função Lua) no servidor tarantool ao qual estamos conectados. Retorna os resultados da invocação.

exemplos de call

Como o console do tarantool é um REPL Lua, qualquer função pode ser invocada desde que esteja disponível no ambiente.

local res, err = tar:call('table.concat', { {'hello', ' ', 'world' } })
-- resposta:
[["hello world"]]
Chamamos a função table.concat da biblioteca table para concatenar a tabela:

{'hello', ' ', 'world' }
A solicitação acima é equivalente à solicitação do console:

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

Para muitos exemplos de procedimentos armazenados do tarantool, veja o repositório; https://github.com/mailru/tarlua

hide_version_header

hide_version_header(<objeto de conexão>)

Por padrão, cada resposta envia um cabeçalho HTTP personalizado X-Tarantool-Version com a versão do servidor tarantool.

X-Tarantool-Version: 1.6.6-191-g82d1bc3

Invocar hide_version_header remove o cabeçalho.

tar:hide_version_header()

Não retorna valores.

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório GitHub do nginx-module-tarantool.