Rails Multibase and Models

Rails 6.1 предлагает возможность использования одновременно нескольких баз данных. Для чего? Во первых можно распределить нагрузку и использовать одну из баз только для чтения, другую только на запись, например для админки. Конечно можно использовать разные типы баз данных в одном приложении. Это открывает еще больше возможностей, например для объединения или миграции данных из разных версий приложения.

Первое с чего начинается настройка - config/database.yml

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %>
  timeout: 5000
  
development:
  primary:
    adapter: postgresql
    encoding: unicode
    host: 127.0.0.1
    port: 5432
    username: postgres
    password: po$tgr$s
    pool: <%= ENV.fetch('RAILS_PG_MAX_THREADS') { 5 } %>
    database: primary_database
  primary_replica:
    adapater: postgresql
    encoding: unicode
    host: 127.0.0.1
    port: 5432
    username: replica_readonly
    password: p@$tgr$s
    pool: 5
    database: primary_database
    replica: true
  secondary:
    adapter: mysql2
    pool: <%= ENV.fetch('RAILS_MY_MAX_THREADS') { 5 } %>
    encoding: utf8
    username: root
    password: r@@t
    host: 127.0.0.1
    port: 3306
    database: secondary_database
    migration_paths: db/secondary_migrate
  lite:
    adapter: sqlite3
    pool: <%= ENV.fetch('RAILS_LT_MAX_THREADS') { 5 } %>
    database: db/development-lite.sqlite3
    migration_paths: db/lite_migrate
    timeout: 5000
    
test:
  <<: *default
  database: db/test.sqlite3
    
  

Для каждого типа данных потребуется установить отдельные пакеты

bundle add sqlite3
bundle add pg
bundle add mysql2
gem 'sqlite3', '~> 1.4'
gem 'pg', '>= 0.18', '< 2.0'
gem 'mysql2', '~> 0.5.3'

Реплицированные базы данных, конечно, должны быть одинаковыми. Пользователь для записи и чтения должен быть разным. Для реплики надо установить replica: true.

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

primary:
  adapter: postgresql
  database: primary_database
  migration_path: db/primary_migration

Для использования разных баз требуется создать отдельный абстрактный класс для каждой базы, который будет основной моделей:

class PrimaryRecord < ActiveRecord::Base
  self.abstract_class = true
  connect_to database: { writing: :primary, reading: :primary }
end

Для ApplicationRecord это может быть

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  connect_to database: { writing: :primary, reading: :primary_replica}
end

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

После создания конфигурации станут доступны дополнительные команды миграции:

# rails db:<cmd>:<database>

rails db:create:primary

rails db:migrate:primary
rails db:migrate:seconadry
rails db:migrate:lite

# ...

Для генератора миграций указывается --database=

rails g migration Post title:string text:text --database=primary
Если абстрактного класса нет, он также будет создан. Можно указать доп. --parent=, если абстрактный класс уже существует  и его имя не соответствует правилам именования.

Для использования реплики только для чтения надо включить автоматическое переключение. В зависимости от HTTP команды будет выбираться необходимая база. Когда приложение получает POST, PUT, DELETE, PATCH запрос то выбирается база для записи, а для GET или HEAD реплика.

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session

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

Официальная документация