Lua и LuaJIT в Tarantool: box, модули, rocks

Рейтинг: 74.1% · 11 голосов
Исчерпывающий курс по Tarantool 3.x: модель данных, движки memtx и vinyl, Lua и файберы, транзакции и MVCC, SQL, конфигурация (box.cfg и декларативная 3.x), репликация и Raft, шардирование vshard, эксплуатация, безопасность. 47 уроков со схемами.
Ответить
Аватара пользователя
denis_tnt
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Lua и LuaJIT в Tarantool: box, модули, rocks

Сообщение denis_tnt »

Оглавление курса (47)
  1. Что такое Tarantool: in-memory СУБД и сервер приложений
  2. Архитектура изнутри: процесс, потоки, event-loop
  3. Установка и первый запуск: tt CLI, пакеты, Docker
  4. Интерактив: консоль, admin-консоль, первые команды
  5. Спейсы и кортежи: форматы, типы данных
  6. Типы индексов и их применимость
  7. Движки хранения: memtx vs vinyl
  8. DDL: схема, создание спейсов и индексов, миграции
  9. DML и выборки: insert/update/upsert, итераторы
  10. Персистентность: WAL, снапшоты, recovery
  11. Внутренности memtx: аллокаторы slab/arena, память
  12. Внутренности vinyl: LSM, компакция, тюнинг
  13. Lua и LuaJIT в Tarantool: box, модули, rocks (вы здесь)
  14. Файберы: кооперативная многозадачность, каналы
  15. Транзакции: ACID, изоляция, MVCC
  16. Хранимые процедуры, модули, организация приложения
  17. net.box: удалённые вызовы, async
  18. Пулы соединений, балансировка, реконнект
  19. Ошибки и диагностика: box.error, pcall
  20. Типы и сериализация: MsgPack, decimal, datetime, uuid
  21. SQL в Tarantool: возможности и связь с box
  22. SQL: таблицы, JOIN, подзапросы, представления
  23. SQL: подготовленные выражения, транзакции, Lua-интероп
  24. Классическая конфигурация box.cfg (legacy 1.x/2.x)
  25. Декларативная конфигурация 3.x: config.yaml, иерархия
  26. Роли и приложения в 3.x
  27. Централизованная конфигурация: etcd / config storage
  28. tt CLI глубоко: разработка, сборка, запуск
  29. Cartridge (официальный legacy) и миграция на 3.x
  30. Репликация: replicaset, топологии
  31. Механика репликации: WAL-стриминг, vclock
  32. Синхронная репликация и выборы лидера (Raft)
  33. Жизненный цикл узла: bootstrap, join, rejoin
  34. vshard: router/storage, виртуальные бакеты
  35. Решардинг и rebalancing бакетов
  36. Запросы поверх шардов: map-reduce, crud
  37. Мониторинг: метрики, Prometheus, Grafana
  38. Логирование и аудит
  39. Бэкапы и восстановление
  40. Безопасность: аутентификация, RBAC, TLS
  41. Производительность: профилирование, тюнинг
  42. Обновления: схема, rolling upgrade
  43. Деплой в продакшен: Docker, топология (официальные паттерны)
  44. Администрирование через официальный TCM (Tarantool Cluster Manager)
  45. Коннекторы: Python, Go, Java
  46. Ключевые модули (rocks): crud, metrics, queue, expirationd
  47. Capstone: шардированный отказоустойчивый кластер
Обзор: зачем СУБД свой язык

Tarantool это не просто база данных, а сервер приложений. Внутри него живёт полноценный интерпретатор Lua, и весь код вашего приложения, и сама конфигурация, и хранимые процедуры это Lua. Поэтому понимать, как устроен стек Lua/LuaJIT/box, важнее, чем знать конкретные функции: от этого зависит и производительность, и то, почему ваш require иногда не находит модуль.

В этом уроке разберём три слоя: язык Lua и его JIT-компилятор LuaJIT, пространство имён box, через которое Lua дотягивается до движка базы, и систему модулей с менеджером rocks.

Механика: три слоя стека

1. LuaJIT как сердце

Tarantool использует не эталонный Lua (PUC-Rio), а LuaJIT 2.1 и притом собственный форк (репозиторий tarantool/luajit), который команда чинит и развивает сама. Это критично: версия языка зафиксирована на Lua 5.1 с отдельными заимствованиями из 5.2/5.3 (например goto, доступными благодаря LuaJIT). Не ждите синтаксиса 5.4, integer-типов из ванильного Lua или bitwise-операторов из 5.3 как языковых конструкций.

LuaJIT это трассирующий JIT. Он не компилирует функции целиком, а наблюдает за горячими циклами и вызовами, записывает реальный путь исполнения (trace) и генерирует машинный код именно под него. Пока код холодный, работает быстрый интерпретатор на ассемблере. Отсюда практический вывод: первые проходы по коду медленнее, разогретый цикл может ускоряться в десятки раз.

2. GC64 и память

В современных сборках включён режим GC64: указатели сборщика мусора 64-битные, и снимается старое ограничение на 1-2 ГБ Lua-памяти. На Linux GC64 включён по умолчанию, исторически на macOS он был выключен. Важно: это про Lua-память (таблицы, строки, замыкания), а не про арену таплов базы, которая управляется отдельным slab-аллокатором и настраивается через memtx_memory.

3. FFI: мост в C без обёрток

LuaJIT несёт FFI (ffi.cdef, ffi.C), позволяющий звать C-функции напрямую, описав их сигнатуру. Многие встроенные модули Tarantool под капотом используют FFI, что даёт почти нулевой оверхед на границе Lua/C. Но FFI-вызовы накладывают ограничения на JIT: некоторые конструкции и реентрантные ситуации (так называемый FFI-сэндвич) приводят к отмене трассы или прерыванию.

4. box как пространство имён движка

box это глобальная таблица, через которую Lua управляет базой: box.cfg{} конфигурирует инстанс, box.space.* даёт доступ к спейсам, box.schema.* меняет схему, box.begin/box.commit управляют транзакциями. Ключевой момент жизненного цикла: до вызова box.cfg{} база ещё не инициализирована, и box.space пуст. Многие функции box становятся доступны только после конфигурации. В Tarantool 3.x с декларативным config.yaml box.cfg вызывается за вас, но семантика та же: сначала конфигурация, потом доступ к данным.

Изображение
Стек Lua/LuaJIT/box и загрузка модулей

Ключевые команды и код

Минимальный модуль

Модуль (он же rock в терминах Lua) это файл, который возвращает таблицу со своим API:

Код: Выделить всё

-- mymodule.lua
local export = {}

function export.greet(name)
    return 'Hello, ' .. name
end

return export
Использование через require, который кэширует результат в package.loaded и при повторном require отдаёт тот же объект:

Код: Выделить всё

local m = require('mymodule')
print(m.greet('Tarantool'))   -- Hello, Tarantool

-- повторный require не перечитывает файл:
print(require('mymodule') == m)  -- true
Как require ищет файлы

require('a.b.c') не работает с точечной нотацией box. Точки в имени превращаются в разделители каталогов, а затем имя подставляется вместо вопросительного знака в шаблонах package.path (для Lua) и package.cpath (для C-библиотек .so):

Код: Выделить всё

-- посмотреть текущие шаблоны поиска
print(package.path)
print(package.cpath)

-- добавить свой каталог ПЕРЕД остальными (важен порядок)
package.path = 'scripts/?.lua;scripts/?/init.lua;' .. package.path
Встроенные модули

Часть функциональности живёт не в box, а в отдельных встроенных модулях, которые тоже подключаются через require, но не требуют установки:

Код: Выделить всё

local fiber   = require('fiber')    -- кооперативные потоки
local fio     = require('fio')      -- файловый ввод-вывод
local json    = require('json')     -- сериализация
local msgpack = require('msgpack')  -- бинарный формат Tarantool
local log     = require('log')      -- логирование
local clock   = require('clock')    -- точное время
Установка внешних rocks через tt

Код: Выделить всё

tt rocks install http          -- HTTP-сервер
tt rocks install metrics       -- метрики мониторинга
tt rocks list                  -- что установлено
tt rocks install vshard 0.1.27 -- конкретная версия
tt использует собственную инсталляцию LuaRocks, подключённую к официальному репозиторию rocks. По умолчанию модули кладутся в каталог .rocks внутри приложения.

Частые заблуждения и грабли
Главная боль новичка: запуск приложения не из корня каталога. Модули из .rocks резолвятся относительно текущего рабочего каталога, и при старте из другого места require их не находит. tt и расширенный загрузчик умеют рекурсивно искать .rocks в родительских каталогах, но если вы зовёте tarantool вручную из чужого каталога, готовьтесь чинить package.path руками.
  • box.cfg меняет рабочий каталог. Если в конфиге задан work_dir, после box.cfg текущий каталог сменится. Все относительные пути в package.path, заданные до этого, могут перестать работать. Добавляйте абсолютные пути или настраивайте package.path после box.cfg.
  • package.path это НЕ системный путь Lua. У Tarantool свой LuaJIT и свои дефолтные пути. Модули, установленные системным luarocks, Tarantool по умолчанию не видит. Ставьте через tt rocks.
  • Это Lua 5.1, а не 5.4. Нет встроенных целых чисел (все number это double, кроме типов Tarantool вроде uint64), нет оператора // и битовых операторов из 5.3 как синтаксиса. Для битов есть модуль bit от LuaJIT.
  • Индексация с 1. Таблицы Lua, поля тапла, аргументы select индексируются с единицы, а не с нуля.
  • require кэширует. Поправили файл модуля, а изменения не видны? Старый объект уже в package.loaded. Для горячей перезагрузки чистят package.loaded['mymodule'].
  • Глобальные переменные опасны. Забытый local создаёт глобал в _G, который живёт вечно и мешает сборщику мусора. Линтер luacheck (тоже rock) ловит это.
Мини-лаба

Создайте файл geo.lua со следующим содержимым и проверьте механику require и кэширования:

Код: Выделить всё

-- geo.lua
local geo = {}
geo.loaded_at = require('clock').time()
function geo.distance(x1, y1, x2, y2)
    return math.sqrt((x2-x1)^2 + (y2-y1)^2)
end
return geo
Запустите интерактивный Tarantool в том же каталоге и выполните:

Код: Выделить всё

local g1 = require('geo')
print(g1.distance(0, 0, 3, 4))     -- ожидаем 5
local g2 = require('geo')
print(g1.loaded_at == g2.loaded_at) -- true, файл прочитан один раз
package.loaded['geo'] = nil         -- сбрасываем кэш
local g3 = require('geo')
print(g1.loaded_at == g3.loaded_at) -- false, перезагрузка
Задание: добавьте в package.path подкаталог lib/ и переместите geo.lua туда, добившись, чтобы require('geo') снова работал без изменения имени модуля.

Контрольные вопросы
  • Чем трассирующий JIT в LuaJIT отличается от пофункциональной компиляции и почему первый проход по горячему циклу медленнее последующих?
  • Что произойдёт с box.space, если обратиться к нему до вызова box.cfg{}, и почему?
  • Из каких двух переменных require берёт пути для Lua-модулей и для C-библиотек, и как в шаблоне подставляется имя модуля?
  • Почему модуль, установленный системным luarocks, не виден из Tarantool, и каким инструментом ставить rocks правильно?
👍6 ❤️1 🔥1 😄 🤔2
Ответить
← Предыдущая глава
Внутренности vinyl: LSM, компакция, тюнинг
Следующая глава →
Файберы: кооперативная многозадачность, каналы

Все главы курса «Tarantool: in-memory СУБД и сервер приложений с нуля до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Tarantool: СУБД и сервер приложений»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость