Ключевые модули (rocks): crud, metrics, queue, expirationd

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

Ключевые модули (rocks): crud, metrics, queue, expirationd

Сообщение 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: шардированный отказоустойчивый кластер
Зачем нужны rocks-модули

Ядро Tarantool даёт примитивы: спейсы, индексы, транзакции, файберы, репликацию, vshard. Но в реальном проекте поверх этих примитивов постоянно повторяются одни и те же задачи: писать в шардированный кластер, не думая о bucket_id; отдавать метрики в Prometheus; держать отложенную обработку задач; чистить протухшие записи. Чтобы не изобретать это в каждом проекте, команда Tarantool сопровождает набор официальных Lua-модулей (rocks), которые ставятся через tt rocks и являются такими же первоклассными компонентами платформы, как box. В этом уроке разбираем четвёрку самых ходовых: crud, metrics, queue, expirationd (и кратко migrations).

Важно понимать общий принцип: rocks - это чистый Lua поверх публичного API box. У них нет магии в ядре. crud дёргает vshard.router, metrics читает box.stat и считает свои коллекторы в памяти, queue хранит задачи в обычном спейсе, expirationd - это просто файбер с pairs по индексу. Понимание этого снимает 90% вопросов про их поведение.

Как это устроено внутри

crud - это слой доступа к данным в шардированном (vshard) кластере. Без crud, чтобы вставить тапл, вы должны сами посчитать bucket_id (обычно от хеша ключа шардирования), записать его в специальное поле тапла и направить запрос через vshard.router.callrw на нужный storage. crud прячет это: его функции работают с точки зрения роутера, сами вычисляют bucket_id и маршрутизируют запрос. Точечные операции (insert, get, replace, update, delete, upsert) идут на один storage - тот, где живёт бакет. А вот select/pairs/count без указания ключа шардирования превращаются в map-reduce: роутер рассылает подзапрос на все storages (map), каждый отдаёт частичный результат, роутер их сливает и сортирует (reduce). Отсюда главное свойство: crud.select по вторичному индексу без условия на ключ шардирования - дорогая операция, потому что опрашивает весь кластер.

metrics - реестр коллекторов в памяти процесса. Четыре типа по модели Prometheus: counter (только растёт), gauge (любое число вверх/вниз), histogram (раскладка по бакетам le), summary (квантили в скользящем окне). Каждый коллектор хранит наблюдения, разрезанные по labels (ключ-значение). Дефолтные метрики (network, memory, slab, fibers, operations, vinyl и т.д.) включаются одной строкой и под капотом читают box.stat/box.info. Экспорт - через плагины (prometheus, json, graphite): плагин зовёт register-колбэки, обходит метрики.collectors() и сериализует. С версии 2.11.1 metrics встроен в Tarantool, а в 3.x его настраивают прямо в конфиге секцией metrics.

Изображение
Экосистема ключевых rocks-модулей поверх ядра

queue - персистентная очередь задач поверх обычного спейса. Каждая задача имеет состояние и движется по конечному автомату: READY (готова к взятию) -> TAKEN (взята воркером) -> DONE/удалена (после ack) либо назад в READY (после release). Воркер вызывает take() - получает задачу и переходит её в TAKEN, держа её эксклюзивно. После успешной обработки - ack() (удаляет задачу), при сбое - release() (возвращает в READY). Типы тюбов (tube): fifo (без персистентности TTL), fifottl (с delay/ttl/ttr/pri), utube/utubettl (с гарантией порядка по подочередям). Ключевой механизм надёжности - ttr (time to run): если воркер взял задачу и упал, не сделав ack, по истечении ttr задача автоматически возвращается в READY и достаётся другому. ttl - срок жизни задачи целиком, delay - отложенный старт.

expirationd - демон протухания. Он создаёт фоновый файбер, который бесконечно идёт по индексу спейса через pairs, на каждом тапле зовёт вашу функцию is_expired(args, tuple); если она вернула true - зовёт process_expired_tuple (по умолчанию delete, но можно архивировать). Файбер кооперативно засыпает, чтобы не блокировать другие транзакции, и регулирует темп: за один проход обрабатывает tuples_per_iteration таплов и растягивает полный скан на full_scan_time. Это не TTL-индекс в ядре - это прикладной обход, поэтому удаление протухших записей всегда отстаёт на величину периода скана.

Команды и короткие примеры

Установка модулей в окружение приложения:

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

tt rocks install crud
tt rocks install metrics
tt rocks install queue
tt rocks install expirationd
crud на роутере (формат таплов задаётся при создании спейса со служебным полем bucket_id):

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

local crud = require('crud')

crud.insert('users', {1, box.NULL, 'alice', 30})
-- точечное чтение по первичному ключу -> один storage
crud.get('users', 1)
-- select с условиями; без ключа шардирования это map-reduce по всем storage
crud.select('users', {{'>=', 'age', 18}}, {first = 100})
crud.count('users', {{'==', 'name', 'alice'}})
metrics - кастомный счётчик и экспорт в Prometheus:

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

local metrics = require('metrics')
metrics.cfg{labels = {alias = 'router-1'}}
metrics.enable_default_metrics()

local logins = metrics.counter('app_logins_total', 'Число логинов')
logins:inc(1, {status = 'ok'})

local prometheus = require('metrics.plugins.prometheus')
-- prometheus.collect_http() отдаёт тело для эндпоинта /metrics
queue - продюсер и консьюмер:

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

local queue = require('queue')
queue.create_tube('emails', 'fifottl', {temporary = false})

-- продюсер: задача стартует через 5 c, живёт 3600 c, на обработку 30 c
queue.tube.emails:put({to = 'a@b.c'}, {delay = 5, ttl = 3600, ttr = 30})

-- консьюмер
local task = queue.tube.emails:take(1)   -- ждать до 1 c
if task then
    local id, status, data = task[1], task[2], task[3]
    -- ... обработка ...
    queue.tube.emails:ack(id)            -- успех; при ошибке :release(id)
end
expirationd - чистим записи старше суток по полю created_at (поле 2):

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

local expirationd = require('expirationd')

local function is_expired(args, tuple)
    return tuple[2] < os.time() - 86400
end

expirationd.start('clean_sessions', box.space.sessions.id, is_expired, {
    tuples_per_iteration = 1000,
    full_scan_time       = 3600,   -- полный проход за час
    force                = false,  -- только на мастере
})
migrations (через tt) - применение версионированных DDL/DML по всему кластеру. Каждая миграция лежит файлом в каталоге migrations, применяется централизованно через etcd/tarantool config storage и фиксируется, чтобы не накатиться дважды:

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

tt migrations publish
tt migrations apply
tt migrations status
Сводка по назначению:

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

модуль        слой         где работает      главная сущность
crud          доступ       router            map-reduce + bucket_id
metrics       observ.      каждый instance   collector + labels
queue         workflow     storage (мастер)  tube + FSM задачи
expirationd   обслуживание мастер спейса     fiber + pairs
migrations    эволюция     центр. config     версионный DDL
Частые заблуждения и грабли
"crud.select быстрый, это же просто select". Нет: без условия на ключ шардирования это map-reduce по всем storage с слиянием на роутере. Дорого по сети и CPU. Фильтруйте по ключу шардирования или по первому полю первичного индекса; ставьте first/after для пагинации, не вычитывайте миллионы таплов разом.
  • crud не равно box. crud.insert на роутере не вызывает box.space:insert локально - он маршрутизирует на storage. На самом storage спейс должен иметь поле bucket_id и индекс по нему.
  • metrics labels с высокой кардинальностью (user_id, url с параметрами) ведут к комбинаторному взрыву временных рядов и могут уронить вашу TSDB. Лейблами делают alias инстанса, метод, шаблон роута - но не уникальные идентификаторы.
  • queue: если воркер не вызвал ack и не упал, а просто завис дольше ttr - задача уйдёт второму воркеру, и вы получите двойную обработку. Делайте обработчики идемпотентными. И помните: задачи живут на мастере; после смены лидера очередь должна продолжиться на новом мастере (нужны временные тюбы false и корректная репликация).
  • expirationd удаляет не мгновенно, а в темпе скана: при большом full_scan_time протухшие записи лежат ещё долго. is_expired зовётся на каждом тапле - тяжёлая логика там бьёт по производительности. По умолчанию задача крутится только на писателе; на реплике стартовать её бессмысленно.
  • Версии: rocks обновляются независимо от ядра. Связка crud + vshort + Tarantool 3.x требует совместимых версий (старые crud не знают про auto-discovery мастера в vshard). Фиксируйте версии в rockspec.
Мини-лаба

Поднимите одиночный инстанс (box.cfg на vinyl или memtx). Установите queue и expirationd. Создайте тюб fifottl emails и спейс sessions(id, created_at). Положите 3 задачи в очередь с разными ttr, возьмите одну через take, не делайте ack и подождите дольше ttr - убедитесь, что задача снова стала READY (проверьте queue.statistics или повторный take). Затем запустите expirationd на sessions с is_expired по created_at старше 10 секунд и tuples_per_iteration = 100; вставьте пару записей с прошлым временем и убедитесь, что они исчезают после прохода файбера. Запишите, через сколько секунд реально удалились (это и есть лаг скана).

Контрольные вопросы
  • Почему crud.select без условия на ключ шардирования - это map-reduce, и что именно делает роутер на фазе reduce?
  • В чём разница между ttl, ttr и delay у задачи в тюбе fifottl, и какой из них спасает от зависшего воркера?
  • Почему expirationd не гарантирует мгновенное удаление протухшего тапла и от каких параметров зависит лаг?
  • Какой тип коллектора metrics выберете для "текущее число активных соединений" и почему не counter?
👍2 ❤️2 🔥1 😄 🤔
Ответить
← Предыдущая глава
Коннекторы: Python, Go, Java
Следующая глава →
Capstone: шардированный отказоустойчивый кластер

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

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

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

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

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