Tarantool CE/EE Documentation portal logo
Support
Updated at July 17, 2026   02:08 PM

Lua cookbook recipes

Here are contributions of Lua programs for some frequent or tricky situations.

You can execute any of these programs by copying the code into a .lua file, and then entering chmod +x ./{program-name}.lua and ./{program-name}.lua on the terminal.

The first line is a "hashbang":

#!/usr/bin/env tarantool

This runs Tarantool Lua application server, which should be on the execution path.

This section contains the following recipes:

Use freely.

See more recipes on Tarantool GitHub.

hello_world.lua

The standard example of a simple program.

#!/usr/bin/env tarantoolprint('Hello, World!')

console_start.lua

Use box.once() to initialize a database (creating spaces) if this is the first time the server has been run. Then use console.start() to start interactive mode.

#!/usr/bin/env tarantool-- Configure databasebox.cfg {    listen = 3313}box.once("bootstrap", function()    box.schema.space.create('tweedledum')    box.space.tweedledum:create_index('primary',        { type = 'TREE', parts = {1, 'unsigned'}})end)require('console').start()

fio_read.lua

Use the fio module to open, read, and close a file.

#!/usr/bin/env tarantoollocal fio = require('fio')local errno = require('errno')local f = fio.open('/tmp/xxxx.txt', {'O_RDONLY' })if not f then    error("Failed to open file: "..errno.strerror())endlocal data = f:read(4096)f:close()print(data)

fio_write.lua

Use the fio module to open, write, and close a file.

#!/usr/bin/env tarantoollocal fio = require('fio')local errno = require('errno')local f = fio.open('/tmp/xxxx.txt', {'O_CREAT', 'O_WRONLY', 'O_APPEND'},    tonumber('0666', 8))if not f then    error("Failed to open file: "..errno.strerror())endf:write("Hello\n");f:close()

ffi_printf.lua

Use the LuaJIT ffi library to call a C built-in function: printf(). (For help understanding ffi, see the FFI tutorial.)

#!/usr/bin/env tarantoollocal ffi = require('ffi')ffi.cdef[[    int printf(const char *format, ...);]]ffi.C.printf("Hello, %s\n", os.getenv("USER"));

ffi_gettimeofday.lua

Use the LuaJIT ffi library to call a C function: gettimeofday(). This delivers time with millisecond precision, unlike the time function in Tarantool's clock module.

#!/usr/bin/env tarantoollocal ffi = require('ffi')ffi.cdef[[    typedef long time_t;    typedef struct timeval {    time_t tv_sec;    time_t tv_usec;} timeval;    int gettimeofday(struct timeval *t, void *tzp);]]local timeval_buf = ffi.new("timeval")local now = function()    ffi.C.gettimeofday(timeval_buf, nil)    return tonumber(timeval_buf.tv_sec * 1000 + (timeval_buf.tv_usec / 1000))end

ffi_zlib.lua

Use the LuaJIT ffi library to call a C library function. (For help understanding ffi, see the FFI tutorial.)

#!/usr/bin/env tarantoollocal ffi = require("ffi")ffi.cdef[[    unsigned long compressBound(unsigned long sourceLen);    int compress2(uint8_t *dest, unsigned long *destLen,    const uint8_t *source, unsigned long sourceLen, int level);    int uncompress(uint8_t *dest, unsigned long *destLen,    const uint8_t *source, unsigned long sourceLen);]]local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")-- Lua wrapper for compress2()local function compress(txt)    local n = zlib.compressBound(#txt)    local buf = ffi.new("uint8_t[?]", n)    local buflen = ffi.new("unsigned long[1]", n)    local res = zlib.compress2(buf, buflen, txt, #txt, 9)    assert(res == 0)    return ffi.string(buf, buflen[0])end-- Lua wrapper for uncompresslocal function uncompress(comp, n)    local buf = ffi.new("uint8_t[?]", n)    local buflen = ffi.new("unsigned long[1]", n)    local res = zlib.uncompress(buf, buflen, comp, #comp)    assert(res == 0)    return ffi.string(buf, buflen[0])end-- Simple test code.local txt = string.rep("abcd", 1000)print("Uncompressed size: ", #txt)local c = compress(txt)print("Compressed size: ", #c)local txt2 = uncompress(c, #txt)assert(txt2 == txt)

ffi_meta.lua

Use the LuaJIT ffi library to access a C object via a metamethod (a method which is defined with a metatable).

#!/usr/bin/env tarantoollocal ffi = require("ffi")ffi.cdef[[typedef struct { double x, y; } point_t;]]local pointlocal mt = {  __add = function(a, b) return point(a.x+b.x, a.y+b.y) end,  __len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,  __index = {    area = function(a) return a.x*a.x + a.y*a.y end,  },}point = ffi.metatype("point_t", mt)local a = point(3, 4)print(a.x, a.y)  --> 3  4print(#a)        --> 5print(a:area())  --> 25local b = a + point(0.5, 8)print(#b)        --> 12.5

print_arrays.lua

Create Lua tables, and print them. Notice that for the 'array' table the iterator function is ipairs(), while for the 'map' table the iterator function is pairs(). (ipairs() is faster than pairs(), but pairs() is recommended for map-like tables or mixed tables.) The display will look like: "1 Apple | 2 Orange | 3 Grapefruit | 4 Banana | k3 v3 | k1 v1 | k2 v2".

#!/usr/bin/env tarantoolarray = { 'Apple', 'Orange', 'Grapefruit', 'Banana'}for k, v in ipairs(array) do print(k, v) endmap = { k1 = 'v1', k2 = 'v2', k3 = 'v3' }for k, v in pairs(map) do print(k, v) end

count_array.lua

Use the '#' operator to get the number of items in an array-like Lua table. This operation has O(log(N)) complexity.

#!/usr/bin/env tarantoolarray = { 1, 2, 3}print(#array)

count_array_with_nils.lua

Missing elements in arrays, which Lua treats as "nil"s, cause the simple "#" operator to deliver improper results. The "print(#t)" instruction will print "4"; the "print(counter)" instruction will print "3"; the "print(max)" instruction will print "10". Other table functions, such as table.sort(), will also misbehave when "nils" are present.

#!/usr/bin/env tarantoollocal t = {}t[1] = 1t[4] = 4t[10] = 10print(#t)local counter = 0for k,v in pairs(t) do counter = counter + 1 endprint(counter)local max = 0for k,v in pairs(t) do if k > max then max = k end endprint(max)

count_array_with_nulls.lua

Use explicit NULL values to avoid the problems caused by Lua's nil == missing value behavior. Although json.NULL == nil is true, all the print instructions in this program will print the correct value: 10.

#!/usr/bin/env tarantoollocal json = require('json')local t = {}t[1] = 1; t[2] = json.NULL; t[3]= json.NULL;t[4] = 4; t[5] = json.NULL; t[6]= json.NULL;t[6] = 4; t[7] = json.NULL; t[8]= json.NULL;t[9] = json.NULLt[10] = 10print(#t)local counter = 0for k,v in pairs(t) do counter = counter + 1 endprint(counter)local max = 0for k,v in pairs(t) do if k > max then max = k end endprint(max)

count_map.lua

Get the number of elements in a map-like table.

#!/usr/bin/env tarantoollocal map = { a = 10, b = 15, c = 20 }local size = 0for _ in pairs(map) do size = size + 1; endprint(size)

swap.lua

Use a Lua peculiarity to swap two variables without needing a third variable.

#!/usr/bin/env tarantoollocal x = 1local y = 2x, y = y, xprint(x, y)

class.lua

Create a class, create a metatable for the class, create an instance of the class. Another illustration is at http://lua-users.org/wiki/LuaClassesWithMetatable.

#!/usr/bin/env tarantool-- define class objectslocal myclass_somemethod = function(self)    print('test 1', self.data)endlocal myclass_someothermethod = function(self)    print('test 2', self.data)endlocal myclass_tostring = function(self)    return 'MyClass <'..self.data..'>'endlocal myclass_mt = {    __tostring = myclass_tostring;    __index = {        somemethod = myclass_somemethod;        someothermethod = myclass_someothermethod;    }}-- create a new object of myclasslocal object = setmetatable({ data = 'data'}, myclass_mt)print(object:somemethod())print(object.data)

garbage.lua

Activate the Lua garbage collector with the collectgarbage function.

#!/usr/bin/env tarantoolcollectgarbage('collect')

fiber_producer_and_consumer.lua

Start one fiber for producer and one fiber for consumer. Use fiber.channel() to exchange data and synchronize. One can tweak the channel size (ch_size in the program code) to control the number of simultaneous tasks waiting for processing.

#!/usr/bin/env tarantoollocal fiber = require('fiber')local function consumer_loop(ch, i)    -- initialize consumer synchronously or raise an error()    fiber.sleep(0) -- allow fiber.create() to continue    while true do        local data = ch:get()        if data == nil then            break        end        print('consumed', i, data)        fiber.sleep(math.random()) -- simulate some work    endendlocal function producer_loop(ch, i)    -- initialize consumer synchronously or raise an error()    fiber.sleep(0) -- allow fiber.create() to continue    while true do        local data = math.random()        ch:put(data)        print('produced', i, data)    endendlocal function start()    local consumer_n = 5    local producer_n = 3    -- Create a channel    local ch_size = math.max(consumer_n, producer_n)    local ch = fiber.channel(ch_size)    -- Start consumers    for i=1, consumer_n,1 do        fiber.create(consumer_loop, ch, i)    end    -- Start producers    for i=1, producer_n,1 do        fiber.create(producer_loop, ch, i)    endendstart()print('started')

socket_tcpconnect.lua

Use socket.tcp_connect() to connect to a remote host via TCP. Display the connection details and the result of a GET request.

#!/usr/bin/env tarantoollocal s = require('socket').tcp_connect('google.com', 80)print(s:peer().host)print(s:peer().family)print(s:peer().type)print(s:peer().protocol)print(s:peer().port)print(s:write("GET / HTTP/1.0\r\n\r\n"))print(s:read('\r\n'))print(s:read('\r\n'))

socket_tcp_echo.lua

Use socket.tcp_connect() to set up a simple TCP server, by creating a function that handles requests and echos them, and passing the function to socket.tcp_server(). This program has been used to test with 100,000 clients, with each client getting a separate fiber.

#!/usr/bin/env tarantoollocal function handler(s, peer)    s:write("Welcome to test server, " .. peer.host .."\n")    while true do        local line = s:read('\n')        if line == nil then            break -- error or eof        end        if not s:write("pong: "..line) then            break -- error or eof        end    endendlocal server, addr = require('socket').tcp_server('localhost', 3311, handler)

getaddrinfo.lua

Use socket.getaddrinfo() to perform non-blocking DNS resolution, getting both the AF_INET6 and AF_INET information for 'google.com'. This technique is not always necessary for tcp connections because socket.tcp_connect() performs socket.getaddrinfo under the hood, before trying to connect to the first available address.

#!/usr/bin/env tarantoollocal s = require('socket').getaddrinfo('google.com', 'http', { type = 'SOCK_STREAM' })print('host=',s[1].host)print('family=',s[1].family)print('type=',s[1].type)print('protocol=',s[1].protocol)print('port=',s[1].port)print('host=',s[2].host)print('family=',s[2].family)print('type=',s[2].type)print('protocol=',s[2].protocol)print('port=',s[2].port)

socket_udp_echo.lua

Tarantool does not currently have a udp_server function, therefore socket_udp_echo.lua is more complicated than socket_tcp_echo.lua. It can be implemented with sockets and fibers.

#!/usr/bin/env tarantoollocal socket = require('socket')local errno = require('errno')local fiber = require('fiber')local function udp_server_loop(s, handler)    fiber.name("udp_server")    while true do        -- try to read a datagram first        local msg, peer = s:recvfrom()        if msg == "" then            -- socket was closed via s:close()            break        elseif msg ~= nil then            -- got a new datagram            handler(s, peer, msg)        else            if s:errno() == errno.EAGAIN or s:errno() == errno.EINTR then                -- socket is not ready                s:readable() -- yield, epoll will wake us when new data arrives            else                -- socket error                local msg = s:error()                s:close() -- save resources and don't wait GC                error("Socket error: " .. msg)            end        end    endendlocal function udp_server(host, port, handler)    local s = socket('AF_INET', 'SOCK_DGRAM', 0)    if not s then        return nil -- check errno:strerror()    end    if not s:bind(host, port) then        local e = s:errno() -- save errno        s:close()        errno(e) -- restore errno        return nil -- check errno:strerror()    end    fiber.create(udp_server_loop, s, handler) -- start a new background fiber    return send

A function for a client that connects to this server could look something like this ...

local function handler(s, peer, msg)    -- You don't have to wait until socket is ready to send UDP    -- s:writable()    s:sendto(peer.host, peer.port, "Pong: " .. msg)endlocal server = udp_server('127.0.0.1', 3548, handler)if not server then    error('Failed to bind: ' .. errno.strerror())endprint('Started')require('console').start()

http_get.lua

Use the http module to get data via HTTP.

#!/usr/bin/env tarantoollocal http_client = require('http.client')local json = require('json')local r = http_client.get('https://api.frankfurter.app/latest?to=USD%2CRUB')if r.status ~= 200 then    print('Failed to get currency ', r.reason)    returnendlocal data = json.decode(r.body)print(data.base, 'rate of', data.date, 'is', data.rates.RUB, 'RUB or', data.rates.USD, 'USD')

http_send.lua

Use the http module to send data via HTTP.

#!/usr/bin/env tarantoollocal http_client = require('http.client')local json = require('json')local data = json.encode({ Key = 'Value'})local headers = { Token = 'xxxx', ['X-Secret-Value'] = '42' }local r = http_client.post('http://localhost:8081', data, { headers = headers})if r.status == 200 then    print 'Success'end

http_server.lua

Use the http rock (which must first be installed) to turn Tarantool into a web server.

#!/usr/bin/env tarantoollocal function handler(self)    return self:render{ json = { ['Your-IP-Is'] = self.peer.host } }endlocal server = require('http.server').new(nil, 8080, {charset = "utf8"}) -- listen *:8080server:route({ path = '/' }, handler)server:start()-- connect to localhost:8080 and see json

http_generate_html.lua

Use the http rock (which must first be installed) to generate HTML pages from templates. The http rock has a fairly simple template engine which allows execution of regular Lua code inside text blocks (like PHP). Therefore there is no need to learn new languages in order to write templates.

#!/usr/bin/env tarantoollocal function handler(self)local fruits = {'Apple', 'Orange', 'Grapefruit', 'Banana'}    return self:render{ fruits = fruits }endlocal server = require('http.server').new(nil, 8080, {charset = "utf8"}) -- nil means '*'server:route({ path = '/', file = 'index.html.lua' }, handler)server:start()

An "HTML" file for this server, including Lua, could look like this (it would produce "1 Apple | 2 Orange | 3 Grapefruit | 4 Banana"). Create a templates directory and put this file in it:

<html><body>    </body></html>

select_all.go

In Go, there is no one-liner to select all tuples from a Tarantool space. Yet you can use a script like this one. Call it on the instance you want to connect to.

cookbook/main.go