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


12

Трохи фону

Я вже багато років використовую дорогоцінний камінь для запуску програми для багаторічної оренди. Зараз нещодавно з'явилася необхідність масштабувати базу даних на окремих хостах, сервер db просто більше не може йти в ногу (і читання, і запис стає занадто багато) - і так, я розширив обладнання до максимуму (присвячений апаратне забезпечення, 64 ядра, 12 Nvm-e накопичувачів у рейді 10, 384 Гбіт та ін.).

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

Зараз я бігаю по рейках 4,2 атм., Скоро переходжу до 5.2. Я можу бачити, що рейки 6 додають підтримку визначень підключення для кожної моделі, однак це насправді не те, що мені потрібно, оскільки у мене є повністю дзеркальна схема бази даних для кожного з моїх 20 орендарів. Як правило, я перемикаю "базу даних" за запитом (в середньому програмному забезпеченні) або за фоновим завданням (додаткове програмне забезпечення sidekiq), однак це в даний час банально і обробляється між дорогоцінним каменем, оскільки він просто встановлює значення search_pathPostgresql і насправді не змінює фактичне з'єднання. При переході на стратегію хостингу для орендарів мені потрібно буде переключити все з'єднання за запитом.

Запитання:

  1. Я розумію, що я можу виконати ActiveRecord::Base.establish_connection(config)запит / фонову роботу - однак, як я також розумію, це запускає абсолютно нове рукостискання з підключенням до бази даних і новий пул db для нерестування в рейках - так? Я думаю, що це було б самогубство, яке зробило б таке накладне на кожен запит на мою заяву.
  2. Тому мені цікаво, чи може хтось побачити варіант з рейками, наприклад, попередньо встановивши декілька (всього 20) підключень / пулів до бази даних (спочатку під час завантаження програми), а потім просто переключитися між цими пулами за запитом? Так що з'єднання db вже зроблені та готові до використання.
  3. Чи все це лише погана погана ідея, і чи варто замість цього шукати інший підхід? Наприклад, 1 екземпляр програми = одне конкретне підключення до одного конкретного орендаря. Або щось інше.

2
guides.rubyonrails.org/active_record_multiple_databases.html Я думаю, що це може вам допомогти
Олексій Голубенко,

1
Вас може зацікавити цей PR в сховищі GitHub Rails, який нещодавно додав саме ту функцію, яка вам потрібна в поточній masterгілці Rails . Чи буде запущений Rails Egde опцією чи зворотним друком цієї функції до вашої поточної версії Rails?
spickermann

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

Відповіді:


4

Як я розумію, для додатків для багаторічних найменувань існує 4 схеми:

1. Виділена модель / Кілька виробничих середовищ

У кожному екземплярі або екземплярі бази даних повністю розміщені різні програми орендарів, і нічого не ділиться між орендарями.

Це додаток на 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)не рукостискання до db за запит, якщо ви правильно його використовуєте. Ви можете перевірити тут і прочитати весь коментар тут .

Що стосується (2), якщо ви не хочете використовувати establish_connection, ви можете використовувати дорогоцінний камінь мультивселенной (він працює на рейки 4.2), або інші дорогоцінні камені. Або, як передбачають інші, ви можете оновити до Rails 6.

Редагувати: Використовується мультисерверний самоцвіт establish_connection. Він додастьdatabase.yml і створить базовий клас, щоб кожен підклас поділяв однакове з'єднання / пул. В основному, це зменшує наші зусилля для використання establish_connection.

Щодо (3), відповідь:

Якщо у вас не так багато орендарів, і ваша заявка є досить складною, пропоную скористатися схемою виділеної моделі. Таким чином, ви використовуєте один екземпляр програми = одне конкретне підключення до одного конкретного орендаря. Не потрібно робити додатки складнішими, додаючи кілька підключень до бази даних.

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

У будь-якому випадку вам потрібно оновити / переписати свою програму, щоб відповідати новій архітектурі.


Привіт, дякую за відповідь. Мені знадобиться трохи часу, щоб фактично перевірити пропозицію, перш ніж я зможу нагородити одну з відповідей щедротою, якщо вони є хорошими рішеннями.
Нільс Крістіан

У мене є пара питань стосовно 1 і 2. 1: Я не впевнений, що розумію ваші посилання. Це те, що ти кажеш, що я можу викликати .establish_connection (config), не роблячи рукостискання / відтворення db-опитування? У такому випадку я не впевнений, як це пояснює два посилання? 2: Для мультиверсальних, чи не це переключення баз даних на модель, а не цілий db-комутатор для всього додатка? Я вважаю, що їх документація є досить невиразною
Нільс Крістіан

Я думаю, що у мене є непорозуміння. Ви не проти допрацювати ці речення? Я розумію , що я міг би зробити ActiveRecord :: Base.establish_connection (конфігурації) на запит / фонове завдання - втім, як і я також розумію, що спускові абсолютно нове з'єднання з базою даних рукостискання , які будуть зроблені і новий дб басейн нереститися в рейках Це припустити, що один запит створить один db пул?
KSD Putra

Я маю на увазі: (1) Я переживаю за ефективність / мережу накладних витрат, коли потрібно дзвонити ActiveRecord :: Base.establish_connection (config) на кожен запит, просто щоб переключитися між різними базами даних / країнами
Niels Kristian

Вам не доведеться турбуватися про накладні витрати. Тепер, якщо ви використовуєте одну БД, у вас є один пул з'єднань (ви можете перевірити посилання про з'єднання у відповіді (1) вище). Якщо ви використовуєте establish_connectionв такій моделі: class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); endі скажіть, що у вас є 5 моделей, ви створюєте 5 пулів підключень до DB_SECOND_TENANT. І кожен басейн трактується однаково. Отже, ви створюєте пул не за запитом, а за кожним запитом establish_connection.
KSD Putra


3

Буквально пару днів тому було додано горизонтальне заточування до Ruby на masterгілці 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

@spickermann я читав ab цей дорогоцінний камінь, не тільки для рейок6?
7urkm3n

@ 7urkm3n Він включений у поточну masterгалузь Rails .
spickermann

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