Возможно ли иметь несколько пулов соединений с базами данных в рельсах для переключения между ними?


12

Немного фона

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

Я подумывал сделать это для каждого арендатора (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 экземпляр приложения = одно конкретное соединение с одним конкретным арендатором. Или что-то другое.

2
guides.rubyonrails.org/active_record_multiple_databases.html Я думаю, что это может помочь вам
Алексей Голубенко

1
Вас может заинтересовать этот PR в репозитории Rails GitHub, который недавно добавил именно ту функцию, которая вам нужна в текущую masterветку Rails . Будет ли запуск Rails Egde вариантом или обратным выводом этой функции в вашу текущую версию Rails?
spickermann

@spickermann ActiveRecord::Base.connected_to(shard: :shard_one) do ... endозначает, что пул будет (повторно) использоваться вместо создания нового соединения каждый раз?
Бен

Ответы:


4

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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


Привет спасибо за ответ Мне понадобится немного времени, чтобы на самом деле проверить предложение, прежде чем я смогу наградить один из ответов за вознаграждение, если они являются хорошими решениями.
Нильс Кристиан

У меня есть пара вопросов по поводу 1 и 2. 1: Я не уверен, что понимаю ваши рекомендации. Что вы говорите, что я могу вызвать .establish_connection (config), не делая рукопожатие БД / воссоздание опроса БД? В таком случае, я не уверен, как две ссылки объясняют это? 2: Для мультивселенной, разве это не переключение базы данных для каждой модели, а не переключение всей базы данных для всего приложения? Я чувствую, что их документация довольно расплывчата
Нильс Кристиан

Я думаю, что у меня есть недоразумение. Вы не против разработать эти предложения? Я понимаю , что я мог бы сделать ActiveRecord :: Base.establish_connection (конфигурации) на запрос / фоновое задание - впрочем, как и я также понимаю, что спусковые совершенно новое соединение с базой данных рукопожатия , которые будут сделаны и новый дб бассейн нереститься в рельсах Это предложить один запрос создать один пул дб?
KSD Путра

Я имею в виду: (1) Я беспокоюсь о производительности / сетевых издержках, когда приходится вызывать ActiveRecord :: Base.establish_connection (config) при каждом запросе, просто для переключения между различными базами данных / странами
Нильс Кристиан,

Вам не нужно беспокоиться о накладных расходах. Теперь, если вы используете одну БД, у вас есть один пул соединений (вы можете проверить ссылку на соединение в ответе (1) выше). Если вы используете establish_connectionв модели, подобной этой: class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); endи скажете, что у вас 5 моделей, вы создаете 5 пулов соединений с DB_SECOND_TENANT. И к каждому пулу относятся одинаково. Таким образом, вы создаете пул не по запросу, а по запросу establish_connection.
KSD Путра

3

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


Спасибо, однако, это кажется довольно далеко от моего варианта использования. Это подразумевало бы переписывание всего приложения для использования этой процедуры везде.
Нильс Кристиан

3

Буквально пару дней назад горизонтальный шардинг был добавлен в masterветку Ruby on Rails на GitHub. В настоящее время эта функция официально не выпущена, но в зависимости от версии 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

Будет ли в этом случае иметь смысл переключаться обратно на соединение по умолчанию? github.com/influitive/apartment#middleware-considerations
Бен

1
Как только вы выйдете из ActiveRecord::Base.connected_to ... doблока, он снова будет использовать соединение по умолчанию.
Спикерманн

@ Spickermann Я читал этот камень, не только для рельсов6?
7urkm3n

@ 7urkm3n Он включен в текущую masterветку Rails .
Спикерманн

Привет спасибо за ответ Мне понадобится немного времени, чтобы на самом деле проверить предложение, прежде чем я смогу наградить один из ответов за вознаграждение, если они являются хорошими решениями.
Нильс Кристиан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.