У Tarantool можна поєднати супершвидку базу даних та додаток для роботи з ними. Ось як це робиться

П'ять років тому я спробував працювати з Tarantool, але тоді мені не зайшов. Але нещодавно я проводив вебінар, де розповідав про Hadoop, про те, як працює MapReduce. Там мені запитали: «А чому під це завдання не використовувати Tarantool?».

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

У Tarantool можна поєднати супершвидку базу даних та додаток для роботи з ними. Ось як це робиться

Що таке 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.

У Tarantool можна поєднати супершвидку базу даних та додаток для роботи з ними. Ось як це робиться

З ним досить зручно працювати. Майже кожен рядок, кожен запис даних може мати абсолютно різні колонки.

Усі спейси ми можемо переглянути за допомогою команди 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>

Індекс:

Первинний індекс треба створювати для будь-якого спейсу, бо без нього нічого не працюватиме. Як і будь-якій базі, ми створюємо перше поле – ID запису.

Частини:

Тут ми вказуємо, що складається з нашого індексу. Він складається з однієї частини – перше поле, яке ми використовуватимемо, тип 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 = false, 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 відновлюється з останнього чекпоїнту і потім накочує все пізніші транзакції до моменту зупинки.

У Tarantool можна поєднати супершвидку базу даних та додаток для роботи з ними. Ось як це робиться

Щоб зробити такий запис, потрібно вказати опцію в налаштуваннях 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 <new-branch-name>

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.

Якщо вам потрібен переїзд у хмару або є питання щодо вашої інфраструктури, сміливо залишайте заявку.

PS У нас є 2 безкоштовних аудиту на місяць, можливо, саме ваш проект буде серед них.

Джерело: habr.com

Додати коментар або відгук