В Tarantool можно совместить супербыструю базу данных и приложение для работы с ними. Вот как просто это делается

Пять лет назад я попробовал работать с Tarantool, но тогда он мне не зашел. Но недавно я проводил вебинар, где рассказывал про Hadoop, про то, как работает MapReduce. Там мне задали вопрос — «А почему под эту задачу не использовать Tarantool?».

Ради любопытства я решил вернуться к нему, протестировать последнюю версию — и на этот раз проект мне очень понравился. Сейчас я покажу, как написать в Tarantool простое приложение, нагружу его и проверю производительность, и вы увидите, как там все легко и круто.

6_g2kb4ctyo8t5etwtgkanfft7i.jpeg

Что такое Tarantool


Tarantool позиционирует себя как сверхбыстрая БД. Туда можно пихать любые данные, какие захотите. Плюс, реплицировать их, шардировать — то есть разбивать огромное количество данных по нескольким серверам и объединять с них результаты — делать отказоустойчивые связки типа «мастер-мастер».

Во вторую очередь, это application-сервер. Вы можете писать на нем свои приложения, работать с данными, например, удалять в фоне старые записи по определенным правилам. Можно написать прямо в Тарантуле Http-сервер, который будет работать с данными: выдавать их количество, записывать туда новые данные и редуцировать это все на мастера.

Я читал статью, как ребята сделали очередь сообщений на 300 строк, которое просто рвет и мечет — у них минимальная производительность от 20 000 сообщений в секунду. Здесь можно действительно развернуться и написать очень большое приложение, и это будут не хранимки, как в PostgreS.

Примерно такой сервер, только простой, я и попробую описать в этой статье.

Установка


Для теста я завел три стандартные виртуальные машины — жесткий диск на 20 гигабайт, Ubuntu 18.04. 2 виртуал CPU и 4 гига памяти.

Мы ставим Tarantool — запускаем башевый скрипт либо добавляем репозиторий и делаем apt get install Tarantool. Ссылка на скрипт — (curl -L https://tarantool.io/installer.sh | VER=2.4 sudo -E bash). У нас нас появляются такие команды, как:

tarantoolctl — основная команда для управления инстансами Тарантула.
/etc/tarantool — здесь лежит вся конфигурация.
var/log/tarantool — здесь лежат логи.
var/lib/tarantool — здесь лежат данные, и дальше они разбиты по инстансам.

Есть папки instance-available и instance-enable — в ней находится то, что будет запускаться — файл конфигурации инстанса с lua кодом, где описано, на каких портах он слушает, какая память ему доступна, настройки движка Vinyl, код, который срабатывает при старте сервера, шардирование, очереди, удаление устаревших данных и так далее.

Инстансы работают как в PostgreS. Например, вы хотите запустить несколько копий базы данных, которая висит на разных портах. Получается, на одном сервере запускаются несколько инстансов баз данных, которые висят на разных портах. У них могут абсолютно разные настройки — один инстанс реализует одну логику, второй — другую.

Управление инстансами


У нас есть команда tarantoolctl, которая позволяет управлять инстансами Тарантула. Например, tarantoolctl check example проверит файл с конфигурацией и скажет — файл is ok, если там нет никаких синтаксических ошибок.

Можно посмотреть статус инстанса — tarantoolctl status example. Таким же образом можно делать start, stop, restart.

Когда instance запущен, к нему можно подключиться двумя способами.

1. Административная консоль

По умолчанию Tarantool открывает сокет, там передается обычный ASCII-текст для управления Тарантулом. Подключение к консоли происходит всегда под пользователем admin, там нет аутентификации, поэтому выносить консольный порт для управления Тарантулом вовне не надо.

Для подключения этим способом надо ввести Tarantoolctl enter instance name. Команда запустит консоль и подключится под пользователем admin. Никогда не выставляйте консольный порт наружу — лучше оставить его юнитсокетом. Тогда возможность подключиться к Тарантулу будет только у тех, у кого есть доступ к записи в сокет.

Этот способ нужен для административных вещей. Для работы с данными используйте второй способ — бинарный протокол.

2. Использование бинарного протокола для подключения к определенному порту

В конфигурации есть директива listen, которая открывает порт для внешних коммуникаций. Этот порт используется с бинарным протоколом, и там включена аутентификация.

Для этого подключения используется tarantoolctl connect to port number. Используя ее, можно подключаться к удаленным серверам, использовать аутентификацию и давать различные права доступа.

Запись данных и модуль Box


Так как Tarantool является и базой данных, и application-сервером, в нем есть различные модули. Нас интересует модуль box — он реализует работу с данными. Когда вы записываете что-то в box, Tarantool пишет данные на диск, сохраняет в памяти или делает с ними что-то еще.

Запись


Например, мы заходим в модуль box и вызываем функцию box.once. Она заставит Tarantool запустить наш код при инициализации сервера. Мы создаем space, в котором будут храниться наши данные.

local function bootstrap()
    local space = box.schema.create_space('example')
    space:create_index('primary')
    box.schema.user.grant('guest', 'read,write,execute', 'universe')

    -- Keep things safe by default
    --  box.schema.user.create('example', { password = 'secret' })
    --  box.schema.user.grant('example', 'replication')
    --  box.schema.user.grant('example', 'read,write,execute', 'space', 'example')
end


После этого мы создаем первичный индекс — primary — по которому можно будет искать данные. По умолчанию, если не указать никаких параметров, то будет использовано первое поле в каждой записи для праймери-индекса.

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

Если сравнивать с обычными базами данных, здесь все достаточно просто. У нас есть space — область, в которой просто хранятся наши данные. Каждая запись называется кортеж. Она пакуется в MessagePack. Это очень прикольный формат — он бинарный и занимает меньше места — 18 байт против 27.

vodeq1s8jofjmyjiifkus6-jzki.jpeg

С ним достаточно удобно работать. Почти каждая строчка, каждая запись данных может иметь абсолютно различные колонки.

Все спейсы мы можем посмотреть с помощью команды Box.space. Чтобы выделить конкретный инстанс — пишем box.space example и получаем полную информацию по нему.

В Tarantool встроено два типа движков: Memory и Vinyl. Memory сохраняет все данные в памяти. Поэтому все работает просто и быстро. Данные дампятся на диск, а также существует механизм write ahead log, поэтому мы ничего не потеряем при падении сервера.

Vinyl хранит данные на диске в более привычном нам виде — то есть можно хранить больше данных, чем у нас есть памяти, и Тарантул будет читать их с диска.

Сейчас мы будем использовать Memory.

unix/:/var/run/tarantool/example.control> box.space.example
---
- engine: memtx
  before_replace: 'function: 0x41eb02c8'
  on_replace: 'function: 0x41eb0568'
  ck_constraint: []
  field_count: 0
  temporary: false
  index:
    0: &0
      unique: true
      parts:
      - type: unsigned
        is_nullable: false
        fieldno: 1
      id: 0
      space_id: 512
      type: TREE
      name: primary
    primary: *0
  is_local: false
  enabled: true
  name: example
  id: 512
...

unix/:/var/run/tarantool/example.control>


Index:

Первичный индекс надо создавать для любого спейса, потому что без него ничего не будет работать. Как и в любой базе, мы создаем первое поле — ID записи.

Parts:

Здесь мы указываем, из чего состоит наш индекс. Он состоит из одной части — первое поле, которое мы будем использовать, тип unsigned — положительное целое число. Насколько я помню из документации, максимальное число, которое может быть — 18 квинтиллионов. Офигенно много.

Дальше мы можем вставлять данные с помощью команды insert.

unix/:/var/run/tarantool/example.control> box.space.example:insert{1, 'test1', 'test2'}
---
- [1, 'test1', 'test2']
...

unix/:/var/run/tarantool/example.control> box.space.example:insert{2, 'test2', 'test3', 'test4'}
---
- [2, 'test2', 'test3', 'test4']
...

unix/:/var/run/tarantool/example.control> box.space.example:insert{3, 'test3'}
---
- [3, 'test3']
...

unix/:/var/run/tarantool/example.control> box.space.example:insert{4, 'test4'}
---
- [4, 'test4']
...

unix/:/var/run/tarantool/example.control>


Первое поле используется как первичный ключ, поэтому оно должно быть уникальное. Количеством колонок мы не ограничены, поэтому мы можем вставить туда сколько угодно данных. Они указываются в формате MessagePack, который я описывал выше.

Вывод данных


Дальше мы можем выводить данные командой select.

Box.example.select с указанием ключа {1} выведет нужную запись. Если мы опустим ключ, то увидим все записи, какие у нас есть. Они все различны по количеству колонок, но здесь в принципе нет понятия колонок — есть номера поля.

Данных может быть абсолютно сколько угодно много. И например, нам нужно искать их по второму полю. Для этого мы делаем новый вторичный индекс.


box.space.example:create_index( ‘secondary’, { type = ‘TREE’, unique = falce, parts = {{field = 2, type =’string’} }}) 


Используем команду Create_index.
Называем его Secondary.

После этого нужно указать параметры. Тип индекса — TREE. Оно может быть не уникальное, поэтому вводим Unique = false.

Затем указываем, из каких частей состоит наш индекс. Field — это номер поля, к которому мы привязываем индекс, и указываем тип string. И вот он создался.

unix/:/var/run/tarantool/example.control> box.space.example:create_index('secondary', { type = 'TREE', unique = false, parts = {{field = 2, type = 'string'}}})
---
- unique: false
  parts:
  - type: string
    is_nullable: false
    fieldno: 2
  id: 1
  space_id: 512
  type: TREE
  name: secondary
...

unix/:/var/run/tarantool/example.control>


Теперь вот таким образом мы можем его вызвать:

unix/:/var/run/tarantool/example.control> box.space.example.index.secondary:select('test1')
---
- - [1, 'test1', 'test2']
...


Сохранение


Если мы рестартанем инстанс и снова попробуем вызвать данные — то увидим, что их нет — все пусто. Так происходит, поскольку Tarantool делает чекпоинты и сохранят данные на диск, но если мы остановим работу до ближайшего сохранения, то потеряем все операции — потому что восстановимся с последнего чекпоинта, который был, например, два часа назад.

Сохранять каждую секунду тоже не выйдет — потому что постоянно дампить на диск по 20 Гб — так себе затея.

Для этого была придумана и реализована концепция write-ahead log. С ее помощью на каждое изменение в данных создается запись в маленьком write-ahead log файле.

Каждая запись до чекпоинта сохраняется в них. Для этих файлов мы выставляем размер — например, 64 мб. Когда он заполняется, запись начинает идти во второй файл. И после рестарта Tarantool восстанавливается из последнего чекпоинта и затем накатывает все более поздние транзакции до момента остановки.

0orvqe6fcfk-cnu09phhvdylpti.jpeg

Чтобы осуществлять такую запись, нужно указать опцию в настройках box.cfg (в файле example.lua):

wal_mode = "write”;


Использование данных


С тем, что мы написали сейчас, вы можете использовать Тарантул для хранения данных, и он будет очень быстро работать как БД. А теперь самая вишенка на торте — что можно с этим со всем делать.

Пишем приложение


Например, напишем для Тарантула такое приложение

Приложение смотрите под спойлером
box.cfg {
    listen = '0.0.0.0:3301';
    io_collect_interval = nil;
    readahead = 16320;
    memtx_memory = 128 * 1024 * 1024; -- 128Mb
    memtx_min_tuple_size = 16;
    memtx_max_tuple_size = 128 * 1024 * 1024; -- 128Mb
    vinyl_memory = 128 * 1024 * 1024; -- 128Mb
    vinyl_cache = 128 * 1024 * 1024; -- 128Mb
    vinyl_max_tuple_size = 128 * 1024 * 1024; -- 128Mb
    vinyl_write_threads = 2;
    wal_mode = "write";
    wal_max_size = 256 * 1024 * 1024;
    checkpoint_interval = 60 * 60; -- one hour
    checkpoint_count = 6;
    force_recovery = true;
    log_level = 5;
    log_nonblock = false;
    too_long_threshold = 0.5;
    read_only   = false
}

local function bootstrap()
    local space = box.schema.create_space('example')
    space:create_index('primary')

    box.schema.user.create('example', { password = 'secret' })
    box.schema.user.grant('example', 'read,write,execute', 'space', 'example')

    box.schema.user.create('repl', { password = 'replication' })
    box.schema.user.grant('repl', 'replication')
end

-- for first run create a space and add set up grants
box.once('replica', bootstrap)

-- enabling console access
console = require('console')
console.listen('127.0.0.1:3302')

-- http config
local charset = {}  do -- [0-9a-zA-Z]
    for c = 48, 57  do table.insert(charset, string.char(c)) end
    for c = 65, 90  do table.insert(charset, string.char(c)) end
    for c = 97, 122 do table.insert(charset, string.char(c)) end
end

local function randomString(length)
    if not length or length <= 0 then return '' end
    math.randomseed(os.clock()^5)
    return randomString(length - 1) .. charset[math.random(1, #charset)]
end

local http_router = require('http.router')
local http_server = require('http.server')
local json = require('json')

local httpd = http_server.new('0.0.0.0', 8080, {
    log_requests = true,
    log_errors = true
})

local router = http_router.new()

local function get_count()
 local cnt = box.space.example:len()
 return cnt
end

router:route({method = 'GET', path = '/count'}, function()
    return {status = 200, body = json.encode({count = get_count()})}
end)

router:route({method = 'GET', path = '/token'}, function()
    local token = randomString(32)
    local last = box.space.example:len()
    box.space.example:insert{ last + 1, token }
    return {status = 200, body = json.encode({token = token})}
end)

prometheus = require('prometheus')

fiber = require('fiber')
tokens_count = prometheus.gauge("tarantool_tokens_count",
                              "API Tokens Count")

function monitor_tokens_count()
  while true do
    tokens_count:set(get_count())
    fiber.sleep(5)
  end
end
fiber.create(monitor_tokens_count)

router:route( { method = 'GET', path = '/metrics' }, prometheus.collect_http)

httpd:set_router(router)
httpd:start()


Мы объявляем некоторую табличку в lua, которая определяет символы. Эта табличка нужна для генерации рандомной строки.

local charset = {}  do -- [0-9a-zA-Z]
    for c = 48, 57  do table.insert(charset, string.char(c)) end
    for c = 65, 90  do table.insert(charset, string.char(c)) end
    for c = 97, 122 do table.insert(charset, string.char(c)) end
end


После этого мы объявляем функцию — randomString и придадим в скобках значение длины.

local function randomString(length)
    if not length or length <= 0 then return '' end
    math.randomseed(os.clock()^5)
    return randomString(length - 1) .. charset[math.random(1, #charset)]
end


Затем мы подключаем http-роутер и http-сервер в наш Тарантул-сервер, JSON, которые будем отдавать клиенту.

local http_router = require('http.router')
local http_server = require('http.server')
local json = require('json')


После этого стартуем на порту 8080 на всех интерфейсах http server, который будет логировать все запросы и ошибки.

local httpd = http_server.new('0.0.0.0', 8080, {
    log_requests = true,
    log_errors = true
})


Дальше мы объявляем route, что если на порт 8080 /count приходит запрос с методом GET, то мы вызываем функцию из одной строчки. Она возвращает статус — 200, 404, 403 или любой другой, какой укажем.

router:route({method = 'GET', path = '/count'}, function()
    return {status = 200, body = json.encode({count = get_count()})}
end)


В теле мы возвращаем json.encode, в ней указываем count и getcount, которая вызывается и показывает количество записей в нашей базе.

Второй метод

router:route({method = 'GET', path = '/token'}, function() 
    local token = randomString(32) 
    local last = box.space.example:len() 
    box.space.example:insert{ last + 1, token } 
    return {status = 200, body = json.encode({token = token})}
end)


Где в строке router: route ({method = 'GET', path = '/token'}, function () мы вызываем функцию и генерируем токен.

Строка local token = randomString (32) — это рандомная строка из 32 символов.
В строке local last = box.space.example: len () мы вытаскиваем последний элемент.
А в строке box.space.example: insert{ last + 1, token } записываем в нашу базу данные, то есть просто увеличиваем ID на 1. Это можно сделать, кстати, не только вот таким корявым способом. В Тарантуле есть сиквенсы для этого дела.

Записываем туда токен.

Таким образом, мы в одном файле написали приложение. В нем можно сразу обращаться с данными, и модуль box сделает за вас всю грязную работу.

Оно слушает http и работает с данными, все находится в едином инстансе — и приложение, и данные. Поэтому все происходит достаточно быстро.

Для запуска мы устанавливаем http модуль:

Как мы это делаем, смотрите под спойлером
root@test2:/# tarantoolctl rocks install http
Installing http://rocks.tarantool.org/http-scm-1.src.rock
Missing dependencies for http scm-1:
   checks >= 3.0.1 (not installed)

http scm-1 depends on checks >= 3.0.1 (not installed)
Installing http://rocks.tarantool.org/checks-3.0.1-1.rockspec

Cloning into 'checks'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 28 (delta 1), reused 16 (delta 1), pack-reused 0
Receiving objects: 100% (28/28), 12.69 KiB | 12.69 MiB/s, done.
Resolving deltas: 100% (1/1), done.
Note: checking out '580388773ef11085015b5a06fe52d61acf16b201'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b 

No existing manifest. Attempting to rebuild...
checks 3.0.1-1 is now installed in /.rocks (license: BSD)

-- The C compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Found TARANTOOL: /usr/include (found version "2.4.2-80-g18f2bc82d")
-- Tarantool LUADIR is /.rocks/share/tarantool/rocks/http/scm-1/lua
-- Tarantool LIBDIR is /.rocks/share/tarantool/rocks/http/scm-1/lib
-- Configuring done
-- Generating done
CMake Warning:
  Manually-specified variables were not used by the project:

    version


-- Build files have been written to: /tmp/luarocks_http-scm-1-V4P9SM/http/build.luarocks
Scanning dependencies of target httpd
[ 50%] Building C object http/CMakeFiles/httpd.dir/lib.c.o
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:32:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c: In function ‘tpl_term’:
/usr/include/tarantool/lauxlib.h:144:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
    (*(B)->p++ = (char)(c)))
    ~~~~~~~~~~~^~~~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:62:7: note: in expansion of macro ‘luaL_addchar’
       luaL_addchar(b, '\\');
       ^~~~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:63:6: note: here
      default:
      ^~~~~~~
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:39:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h: In function ‘tpe_parse’:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h:147:9: warning: this statement may fall through [-Wimplicit-fallthrough=]
    type = TPE_TEXT;
    ~~~~~^~~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/tpleval.h:149:3: note: here
   case TPE_LINECODE:
   ^~~~
In file included from /tmp/luarocks_http-scm-1-V4P9SM/http/http/lib.c:40:0:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h: In function ‘httpfast_parse’:
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:372:22: warning: this statement may fall through [-Wimplicit-fallthrough=]
                 code = 0;
                 ~~~~~^~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:374:13: note: here
             case status:
             ^~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:393:23: warning: this statement may fall through [-Wimplicit-fallthrough=]
                 state = message;
                 ~~~~~~^~~~~~~~~
/tmp/luarocks_http-scm-1-V4P9SM/http/http/httpfast.h:395:13: note: here
             case message:
             ^~~~
[100%] Linking C shared library lib.so
[100%] Built target httpd
[100%] Built target httpd
Install the project...
-- Install configuration: "Debug"
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/VERSION.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lib/http/lib.so
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/server/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/server/tsgi_adapter.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/nginx_server/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/init.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/fs.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/matching.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/middleware.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/request.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/router/response.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/tsgi.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/utils.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/mime_types.lua
-- Installing: /.rocks/share/tarantool/rocks/http/scm-1/lua/http/codes.lua
http scm-1 is now installed in /.rocks (license: BSD)

root@test2:/#


Также для запуска нам потребуется prometheus:

root@test2:/# tarantoolctl rocks install prometheus
Installing http://rocks.tarantool.org/prometheus-scm-1.rockspec

Cloning into 'prometheus'...
remote: Enumerating objects: 19, done.
remote: Counting objects: 100% (19/19), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 19 (delta 2), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (19/19), 10.73 KiB | 10.73 MiB/s, done.
Resolving deltas: 100% (2/2), done.
prometheus scm-1 is now installed in /.rocks (license: BSD)

root@test2:/#


Запускаем и можем обращаться к модулям

root@test2:/# curl -D - -s http://127.0.0.1:8080/token
HTTP/1.1 200 Ok
Content-length: 44
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive

{"token":"e2tPq9l5Z3QZrewRf6uuoJUl3lJgSLOI"}

root@test2:/# curl -D - -s http://127.0.0.1:8080/token
HTTP/1.1 200 Ok
Content-length: 44
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive

{"token":"fR5aCA84gj9eZI3gJcV0LEDl9XZAG2Iu"}

root@test2:/# curl -D - -s http://127.0.0.1:8080/count
HTTP/1.1 200 Ok
Content-length: 11
Server: Tarantool http (tarantool v2.4.2-80-g18f2bc82d)
Connection: keep-alive

{"count":2}root@test2:/#


/count отдает нам статус 200.
/token выдает токен и делает запись этого токена в базу.

Тестируем скорость


Давайте запустим бенчмарк на 50 000 запросов. Конкурентных запросов будет 500.

root@test2:/# ab -c 500 -n 50000 http://127.0.0.1:8080/token
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Completed 50000 requests
Finished 50000 requests


Server Software:        Tarantool
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /token
Document Length:        44 bytes

Concurrency Level:      500
Time taken for tests:   14.578 seconds
Complete requests:      50000
Failed requests:        0
Total transferred:      7950000 bytes
HTML transferred:       2200000 bytes
Requests per second:    3429.87 [#/sec] (mean)
Time per request:       145.778 [ms] (mean)
Time per request:       0.292 [ms] (mean, across all concurrent requests)
Transfer rate:          532.57 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   10 103.2      0    3048
Processing:    12   69 685.1     15   13538
Waiting:       12   69 685.1     15   13538
Total:         12   78 768.2     15   14573

Percentage of the requests served within a certain time (ms)
  50%     15
  66%     15
  75%     16
  80%     16
  90%     16
  95%     16
  98%     21
  99%     42
 100%  14573 (longest request)
root@test2:/#


Токены выписываются. И мы постоянно записываем данные. 99% запросов отработали за 42 миллисекунды. Соответственно, у нас порядка 3500 запросов в секунду на маленькой машинке, где 2 ядра и 4 гигабайта памяти.

Также можно заселектить какой-нибудь 50000-токен и посмотреть его значение.

Можно использовать не только http, запускать бэкгрануд-функции, которые обрабатывают ваши данные. Плюс есть различные триггеры. Например, вы можете вызывать функции на апдейтах, что-то проверять — исправлять конфликты.

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

Аппликейшн-сервер может обращаться к внешним серверам, забирать данные и складывать к себе в базу. Данные из этой базы будут использовать другие приложения.

Это будет делать сам Тарантул, и не придется писать отдельное приложение.

В заключение


Это только первая часть большой работы. Вторая будет опубликована совсем скоро в блоге Mail.ru Group, и мы обязательно добавим ссылку на нее в этот материал.

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

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

P.S. У нас есть 2 бесплатных аудита в месяц, возможно, именно ваш проект будет в их числе.

© Habrahabr.ru