Возможно ли иметь несколько пулов соединений с базами данных в рельсах для переключения между ними? - PullRequest
12 голосов
/ 26 февраля 2020

Небольшой фон

Я использую Жемчужина квартиры для запуска многопользовательского приложения в течение многих лет. В последнее время возникла необходимость масштабирования базы данных на отдельные хосты, сервер БД просто не может больше поддерживать (и при чтении, и при записи получается слишком много) - и да, я масштабировал оборудование до максимума (выделенный аппаратное обеспечение, 64 ядра, 12 дисков Nvm-e в raid 10, 384 Гб оперативной памяти и т. д. c.).

Я рассматривал возможность сделать это для каждого арендатора (1 клиент = 1 конфигурация / пул подключения к базе данных), так как это был бы "простой" и эффективный способ увеличить пропускную способность в number-of-tenants раз без нагрузки изменений кода приложения.

Сейчас я бегу по рельсам 4,2 атм., Скоро обновлюсь до 5,2. Я вижу, что в rails 6 добавлена ​​поддержка определений соединений для каждой модели, однако это не совсем то, что мне нужно, поскольку у меня есть полностью зеркальная схема базы данных для каждого из моих 20 арендаторов. Обычно я переключаю «базу данных» на запрос (в промежуточном программном обеспечении) или на фоновое задание (sidekiq middleware), однако в настоящее время это тривиально и обрабатывается в самоцвете «Квартира», поскольку оно просто устанавливает search_path в Postgresql и не меняет ничего на самом деле фактическое соединение. При переходе на хост-стратегию для каждого арендатора мне нужно будет переключать все соединение для каждого запроса.

Вопросы:

  1. Я понимаю, что могу сделать ActiveRecord::Base.establish_connection(config) за запрос / фоновое задание - однако, насколько я понимаю, это вызывает совершенно новое установление связи с базой данных и создание нового пула базы данных в рельсах - верно? Я полагаю, что это было бы самоубийством из-за производительности при каждом таком запросе к моему приложению.
  2. Поэтому мне интересно, может ли кто-нибудь увидеть вариант с рельсами, например, предварительно установив несколько (всего 20 ) соединения с базой данных / пулы с самого начала (например, при загрузке приложения), а затем просто переключаться между этими пулами по запросу? Так что он подключен к БД уже готов и готов к использованию.
  3. Является ли это всего лишь плохой плохой идеей, и должен ли я вместо этого искать другой подход? Например, 1 экземпляр приложения = одно указанное c подключение к одному указанному c арендатору. Или что-то другое.

Ответы [ 3 ]

4 голосов
/ 03 марта 2020

Как я понимаю, для мультитенантного приложения существует 4 шаблона:

1. Выделенная модель / несколько производственных сред

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

Это приложение на 1 экземпляр и 1 база данных для 1 арендатора. Разработка будет простой, как если бы вы обслуживали только одного арендатора. Но для девопов будет кошмар, если у вас, скажем, 100 арендаторов.

2. Физическое разделение арендаторов

1 экземпляр приложения для всех арендаторов, но 1 база данных для 1 арендатора. Это то, что вы ищете. Вы можете использовать ActiveRecord::Base.establish_connection(config), или использовать гемы, или обновить до Rails 6, как предлагают другие. См. Ответ для (2) ниже.

3. Модель изолированной схемы / логические сегрегации

В изолированной схеме таблицы арендаторов или компоненты базы данных группируются в рамках логической схемы или пространства имен и отделяются от других схем арендаторов, однако схема размещается в тот же экземпляр базы данных.

1 приложение экземпляра и 1 база данных для всех арендаторов, как вы делаете с квартирой gem.

4. Частично изолированный компонент

В этой модели компоненты, имеющие общие функции, совместно используются арендаторами, в то время как компоненты с уникальными или несвязанными функциями являются изолированными. На уровне данных общие данные, такие как данные, идентифицирующие арендаторов, группируются или хранятся в одной таблице, в то время как данные арендатора c изолируются на уровне таблицы или экземпляра.


Что касается (1), ActiveRecord::Base.establish_connection(config) не рукопожатие в дБ за запрос, если вы используете его правильно. Вы можете проверить здесь и прочитать все комментарии здесь .

Что касается (2), если вы не хотите использовать establish_connection, вы можете использовать gem multiverse (работает для рельсов 4.2) или другие драгоценные камни. Или, как предлагают другие, вы можете обновить до Rails 6.

Редактировать: Multiverse gem использует establish_connection. Он добавит database.yml и создаст базовый класс так, чтобы каждый подкласс разделял одно и то же соединение / пул. По сути, это сокращает наши усилия по использованию establish_connection.

Что касается (3), ответ:

Если у вас не так много арендаторов, а ваше приложение довольно сложное, Я предлагаю вам использовать шаблон Dedicated Model. Таким образом, вы go для 1 экземпляра приложения = одно указанное c подключение к одному указанному c арендатору. Вам не нужно усложнять свои приложения, добавляя несколько соединений с базой данных.

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

В любом случае, вы должны обновить / переписать ваше приложение, чтобы соответствовать новой архитектуре.

3 голосов
/ 04 марта 2020

Всего за пару дней в ветку Ruby Rails * на GitHub было добавлено go горизонтальное разбиение . В настоящее время эта функция официально не выпущена, но в зависимости от версии Rails вашего приложения вы можете рассмотреть возможность использования Rails master, добавив ее в Gemfile:

gem "rails", github: "rails/rails", branch: "master"

С помощью этой новой функции вы можете Воспользуйтесь преимуществами пула соединений с базой данных Rails и переключите базу данных в зависимости от условий.

Я не использовал эту новую функцию, но она кажется довольно простой:

# in your config/database.yml
production:
  primary:
    database: my_database
    # other config: user, password, etc
  primary_tenant_1:
    database: tenant_1_database
    # other config: user, password, etc

# in your controller for example when updating a tenant
ActiveRecord::Base.connected_to(shard: "primary_tenant_#{tenant.database_shard_number}") do
  tenant.save
end

Вы не сделали не добавляйте подробностей о том, как вы определяете номер арендатора или как выполняется авторизация в вашем приложении. Но я бы попытался определить номер арендатора как можно скорее в application_controller в around_action. Примерно так может быть отправной точкой:

around_filter :determine_database_connection

private

def determine_database_connection
  # assuming you have a method to determine the current_tenant and that tenant
  # has a method that returns the number of the shard to use or even the 
  # full shard identifier
  shard = current_tenant.database_shard # returns for example `:primary_tenant_1` 

  ActiveRecord::Base.connected_to(shard: shard) do
    yield
  end
end
3 голосов
/ 27 февраля 2020

Насколько я понимаю, (2) должно быть возможно при ручном переключении соединения в Rails 6.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...