rails
May 11, 2023

Gorynich

О чём поговорим?

В статье я расскажу про гем Gorynich, написанный в нашей компании для создания мультитенантных приложений.

Вы узнаете:

  • что делает гем;
  • зачем его создали;
  • как его конфигурировать;
  • как его использовать.

Что такое Gorynich?

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

Поскольку мультитенантное приложение тесно связано с разделением данных, которые в свою очередь могут находиться в разных источниках (СУБД, S3, Redis и пр.), а также с их обработкой в разных подсистемах (ActiveJob, ActionCable), мы выбрали название "Горыныч", чтобы подчеркнуть многоголовость многогранность интеграций.

Немного теории

Мультитенантность (или мультиарендность) — архитектурный способ построения ПО, где единый экземпляр приложения обслуживает несколько клиентов («арендаторов»). Главным преимуществом такого подхода является экономия ресурсов: экземпляр приложения обычно берёт на себя определенную долю памяти и вычислительных расходов, которые могут быть существенными при умножении на большое количество клиентов. Мультитенантность уменьшает эти вычислительные расходы, амортизируя их на большинство клиентов.

Плюсы мультитенантности:

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

Минусы мультитенантности:

  • отказоустойчивость: мультитенатное приложение более уязвимо к сбою экземпляра, чем выделенное приложение. Сбой выделенного развертывания влияет только на клиента, который использует данное приложение, тогда как сбой мультитенатного приложения затронет всех клиентов,
  • сложнее разрабатывать: требует дополнительных ресурсов тестирования безопасности и изоляции клиентов (и вот это мы решили исправить).

Как и любые минусы, это всё решаемо, но об этом надо отдельно задумываться.

Как мы решили создать Gorynich

Когда после долгих обсуждений было принято решение, что проект будет мультитенантным, мы начали искать инструменты. Из того, что мы нашли:

  • гем apartment
  • только появился Rails 6 с возможностью подключения к разным БД

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

В чём преимущества гема Gorynich перед решениями, что мы нашли:

  • простота использования: после конфигурации вам не надо думать про то, что у вас несколько баз данных, просто пишите код, как для обычного Rails приложения,
  • работает не только для ActiveRecord, но и для других частей Rails (ActiveJob, ActionCable),
  • возможность использовать разные источники данных для конфигурации (сейчас реализованы "Файл" и "консул").

Установка Gorynich

Как и любой другой гем для Rails, его нужно добавить в Gemfile:

gem 'gorynich'

И выполнить:

bundle install

Гем установлен! Теперь его нужно сконфигурировать. Для начала нужно выбрать источник данных для конфигурации. На момент создания статьи были реализованы "Файл" и "консул". Пример структуры конфигурации:

Важно! Каждое окружение должно иметь тенант default. Это сервисный тенант, в котором выполняются job-ы (ActiveJob), которые в свою очередь внутри себя переходят в окружение использующегося тенанта. Если вы используйте backend-ы для ActiveJob, такие как DelayedJob, то информация о job-е, включая информацию о тенанте, в которой она создалась, сохраняется в базу тенанта default.

Для того чтобы подключить источник конфигурации, его нужно прописать в файле application.rb (или в файлах окружений):

Также необходимо сказать Rails, что ваше приложение будет использовать несколько БД. Для этого database.yml должен выглядеть так:

Но что делать, если вам нужна какая-то дополнительная БД, которая не является тенантом, например какая-нибудь общая БД для всех тенантов? Тогда database.yml должен выглядеть следующим образом:

По умолчанию Gorynich работает только с ActiveRecord. Если вы хотите включить дополнительные модули (из коробки ActionCable и ActiveJob), то необходимо в initializers добавить файл gorynich.rb. Это можно сделать с помощью команды:

rails g gorynich:install

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

Настроили. Что дальше? Дальше пишите код, как будто у вас немультитенантный проект. Но если вам нужно пробежаться по всем тенантам или переключиться в какой-либо другой, вы можете использовать следующие методы:

Также для правильной работы с Rails console вам необходимо использовать команду

TENANT=tenant rails gc # по умолчанию TENANT = default

Итог

После внедрения и использования (около 3-х лет) в наших проектах гема "Gorynich", мы решили вынести его в open source. Будем рады предложениям и замечаниям, мы с огромным интересом их рассмотрим 🤞.