다중 데이터베이스와 Active Record
이 가이드는 Rails 애플리케이션에서 다중 데이터베이스를 사용하는 방법을 다룹니다.
이 가이드를 읽고 나면 다음을 알 수 있습니다:
- 애플리케이션을 다중 데이터베이스로 설정하는 방법
- 자동 연결 전환 작동 방식
- 다중 데이터베이스를 위한 수평 분할 사용 방법
- 지원되는 기능과 진행 중인 작업
애플리케이션의 인기와 사용량이 늘어남에 따라 새로운 사용자와 데이터를 지원하도록 애플리케이션을 확장해야 합니다. 애플리케이션을 확장해야 할 부분 중 하나가 데이터베이스 수준입니다. Rails는 다중 데이터베이스 사용을 지원하므로 데이터를 한 곳에 저장할 필요가 없습니다.
현재 다음과 같은 기능이 지원됩니다:
- 각각의 복제본을 가진 다중 작성기 데이터베이스
- 작업 중인 모델에 대한 자동 연결 전환
- HTTP 동사와 최근 작성에 따른 작성기와 복제본 간 자동 전환
- 다중 데이터베이스 생성, 삭제, 마이그레이션, 상호 작용을 위한 Rails 작업
다음과 같은 기능은 (아직) 지원되지 않습니다:
- 복제본 로드 밸런싱
애플리케이션 설정
Rails는 대부분의 작업을 대신 해주지만, 애플리케이션을 다중 데이터베이스로 준비하기 위해 몇 가지 단계를 수행해야 합니다.
단일 작성기 데이터베이스가 있는 애플리케이션에 새로운 데이터베이스를 추가해야 한다고 가정해 보겠습니다. 새로운 데이터베이스의 이름은 “animals"입니다.
database.yml
은 다음과 같습니다:
production: database: my_primary_database adapter: mysql2 username: root password: <%= ENV['ROOT_PASSWORD'] %>
이제 "animals"라는 두 번째 데이터베이스와 두 데이터베이스 모두에 대한 복제본을 추가해 보겠습니다. 이를 위해 2단계 구성에서 3단계 구성으로 database.yml
을 변경해야 합니다.
기본 구성이 제공되면 "기본” 구성으로 사용됩니다. “primary” 구성이 없는 경우 Rails는 각 환경의 첫 번째 구성을 기본값으로 사용합니다. 기본 구성은 기본 Rails 파일 이름을 사용합니다. 예를 들어 기본 구성은 스키마 파일로 schema.rb
를 사용하지만, 다른 모든 항목은 [CONFIGURATION_NAMESPACE]_schema.rb
를 사용합니다.
production: primary: database: my_primary_database username: root password: <%= ENV['ROOT_PASSWORD'] %> adapter: mysql2 primary_replica: database: my_primary_database username: root_readonly password: <%= ENV['ROOT_READONLY_PASSWORD'] %> adapter: mysql2 replica: true animals: database: my_animals_database username: animals_root password: <%= ENV['ANIMALS_ROOT_PASSWORD'] %> adapter: mysql2 migrations_paths: db/animals_migrate animals_replica: database: my_animals_database username: animals_readonly password: <%= ENV['ANIMALS_READONLY_PASSWORD'] %> adapter: mysql2 replica: true
다중 데이터베이스를 사용할 때 몇 가지 중요한 설정이 있습니다.
첫째, primary
와 primary_replica
의 데이터베이스 이름은 같아야 합니다. 이는 동일한 데이터를 포함하기 때문입니다. animals
와 animals_replica
도 마찬가지입니다.
둘째, 작성기와 복제본의 사용자 이름은 달라야 하며, 복제본 사용자의 데이터베이스 권한은 읽기만 가능하도록 설정해야 합니다.
복제 데이터베이스를 사용할 때는 database.yml
에 replica: true
항목을 추가해야 합니다. 그렇지 않으면 Rails에서 어떤 것이 복제본이고 어떤 것이 작성기인지 알 수 없습니다. Rails는 마이그레이션과 같은 특정 작업을 복제본에서 실행하지 않습니다.
마지막으로 새 작성기 데이터베이스의 경우 migrations_paths
를 해당 데이터베이스의 마이그레이션이 저장된 디렉토리로 설정해야 합니다. 이 가이드의 후반부에서 migrations_paths
에 대해 자세히 살펴보겠습니다.
또한 schema_dump
옵션을 사용하여 스키마 덤프 파일을 구성하거나 schema_dump: false
를 설정하여 스키마 덤프를 완전히 건너뛸 수 있습니다.
이제 새 데이터베이스가 준비되었으므로 연결 모델을 설정해 보겠습니다. 새 데이터베이스를 사용하려면 새 추상 클래스를 만들고 animals 데이터베이스에 연결해야 합니다.
class AnimalsRecord < ApplicationRecord self.abstract_class = true connects_to database: { writing: :animals, reading: :animals_replica } end
그런 다음 ApplicationRecord
를 업데이트하여 새 복제본을 인식하도록 해야 합니다.
class ApplicationRecord < ActiveRecord::Base self.abstract_class = true connects_to database: { writing: :primary, reading: :primary_replica } end
애플리케이션 레코드에 다른 이름의 클래스를 사용하는 경우 primary_abstract_class
를 대신 설정해야 합니다. 그래야 Rails가 ActiveRecord::Base
가 어떤 클래스와 연결을 공유해야 하는지 알 수 있습니다.
class PrimaryApplicationRecord < ActiveRecord::Base primary_abstract_class end
기본 추상 클래스에서 상속하는 클래스는 표준 Rails 애플리케이션과 같습니다:
class Person < ApplicationRecord end
기본적으로 Rails는 기본 및 복제본에 대해 각각 writing
과 reading
데이터베이스 역할을 예상합니다. 기존 시스템에 이미 설정된 역할이 있고 변경하고 싶지 않은 경우 애플리케이션 구성에서 새 역할 이름을 설정할 수 있습니다.
config.active_record.writing_role = :default config.active_record.reading_role = :readonly
단일 모델에 데이터베이스에 연결하고 해당 모델에서 테이블을 상속하는 것이 중요합니다. 데이터베이스 클라이언트에는 열 수 있는 연결 수에 제한이 있으며, 이렇게 하면 Rails가 모델 클래스 이름을 연결 사양 이름으로 사용하므로 연결 수가 늘어납니다.
이제 database.yml
과 새 모델이 설정되었으므로 데이터베이스를 생성할 차례입니다. Rails에는 다중 데이터베이스를 사용하는 데 필요한 모든 명령이 포함되어 있습니다.
bin/rails --help
를 실행하면 사용할 수 있는 모든 명령을 볼 수 있습니다. 다음과 같은 내용이 표시되어야 합니다:
$ bin/rails --help ... db:create # Create the database from DATABASE_URL or config/database.yml for the ... db:create:animals # Create animals database for current environment db:create:primary # Create primary database for current environment db:drop # Drop the database from DATABASE_URL or config/database.yml for the cu... db:drop:animals # Drop animals database for current environment db:drop:primary # Drop primary database for current environment db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog) db:migrate:animals # Migrate animals database for current environment db:migrate:primary # Migrate primary database for current environment db:migrate:status # Display status of migrations db:migrate:status:animals # Display status of migrations for animals database db:migrate:status:primary # Display status of migrations for primary database db:reset # Drop and recreates all databases from their schema for the current environment and loads the seeds db:reset:animals # Drop and recreates the animals database from its schema for the current environment and loads the seeds db:reset:primary # Drop and recreates the primary database from its schema for the current environment and loads the seeds db:rollback # Roll the schema back to the previous version (specify steps w/ STEP=n) db:rollback:animals # Rollback animals database for current environment (specify steps w/ STEP=n) db:rollback:primary # Rollback primary database for current environment (specify steps w/ STEP=n) db:schema:dump # Create a database schema file (either db/schema.rb or db/structure.sql ... db:schema:dump:animals # Create a database schema file (either db/schema.rb or db/structure.sql ... db:schema:dump:primary # Create a db/schema.rb file that is portable against any DB supported ... db:schema:load # Load a database schema file (either db/schema.rb or db/structure.sql ... db:schema:load:animals # Load a database schema file (either db/schema.rb or db/structure.sql ... db:schema:load:primary # Load a database schema file (either db/schema.rb or db/structure.sql ... db:setup # Create all databases, loads all schemas, and initializes with the seed data (use db:reset to also drop all databases first) db:setup:animals # Create the animals database, loads the schema, and initializes with the seed data (use db:reset:animals to also drop the database first) db:setup:primary # Create the primary database, loads the schema, and initializes with the seed data (use db:reset:primary to also drop the database first) ...
bin/rails db:create
명령을 실행하면 primary와 animals 데이터베이스가 모두 생성됩니다. 데이터베이스 사용자를 생성하는 명령은 없으며, 복제본의 읽기 전용 사용자를 지원하려면 수동으로 수행해야 합니다. animals 데이터베이스만 생성하려면 bin/rails db:create:animals
를 실행할 수 있습니다.
스키마 및 마이그레이션 관리 없이 데이터베이스에 연결
외부 데이터베이스에 연결하되 스키마 관리, 마이그레이션, 시드 등의 데이터베이스 관리 작업을 수행하고 싶지 않은 경우 데이터베이스별 구성 옵션 database_tasks: false
를 설정할 수 있습니다. 기본적으로 이 옵션은 true로 설정되어 있습니다.
production: primary: database: my_database adapter: mysql2 animals: database: my_animals_database adapter: mysql2 database_tasks: false
생성기 및 마이그레이션
다중 데이터베이스의 마이그레이션은 구성의 데이터베이스 키 이름으로 시작하는 고유 폴더에 있어야 합니다.
또한 데이터베이스 구성에서 migrations_paths
를 설정하여 Rails가 마이그레이션을 찾을 수 있도록 해야 합니다.
예를 들어 animals
데이터베이스는 db/animals_migrate
디렉토리에서 마이그레이션을 찾고, primary
는 db/migrate
에서 찾습니다. Rails 생성기는 이제 --database
옵션을 사용하므로 파일이 올바른 디렉토리에 생성됩니다. 다음과 같이 명령을 실행할 수 있습니다:
$ bin/rails generate migration CreateDogs name:string --database animals
Rails 생성기를 사용하는 경우 스캐폴드와 모델 생성기가 추상 클래스를 자동으로 생성합니다. 명령줄에 데이터베이스 키를 전달하기만 하면 됩니다.
$ bin/rails generate scaffold Dog name:string --database animals
데이터베이스 이름과 Record
가 포함된 클래스가 생성됩니다. 이 예에서 데이터베이스는 Animals
이므로 AnimalsRecord
가 생성됩니다:
class AnimalsRecord < ApplicationRecord self.abstract_class = true connects_to database: { writing: :animals } end
생성된 모델은 자네, 번역을 계속하겠습니다.
자동으로 AnimalsRecord
를 상속합니다.
class Dog < AnimalsRecord end
참고: Rails는 작성기에 대한 복제본이 어떤 것인지 알 수 없으므로 작업이 완료된 후 추상 클래스에 이를 추가해야 합니다.
Rails는 새 클래스를 한 번만 생성합니다. 새 스캐폴드로 인해 덮어쓰이지 않으며, 스캐폴드가 삭제되어도 삭제되지 않습니다.
추상 클래스의 이름이 AnimalsRecord
와 다른 경우 --parent
옵션을 전달하여 다른 추상 클래스를 사용하도록 지정할 수 있습니다:
$ bin/rails generate scaffold Dog name:string --database animals --parent Animals::Record
이렇게 하면 다른 부모 클래스를 사용하도록 지정했기 때문에 AnimalsRecord
를 생성하지 않습니다.
자동 역할 전환 활성화
마지막으로 애플리케이션에서 읽기 전용 복제본을 사용하려면 미들웨어를 활성화하여 자동 전환을 수행해야 합니다.
자동 전환을 통해 애플리케이션은 HTTP 동사와 요청 사용자의 최근 작성 여부에 따라 작성기와 복제본 간에 전환할 수 있습니다.
애플리케이션이 POST, PUT, DELETE 또는 PATCH 요청을 받으면 자동으로 작성기 데이터베이스에 기록됩니다. 이러한 메서드가 아닌 요청이지만 애플리케이션이 최근에 작성한 경우에도 작성기 데이터베이스가 사용됩니다. 그 외의 모든 요청은 복제본 데이터베이스를 사용합니다.
자동 연결 전환 미들웨어를 활성화하려면 자동 전환 생성기를 실행할 수 있습니다:
$ bin/rails g active_record:multi_db
그리고 다음 줄의 주석을 제거합니다:
Rails.application.configure do 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 end
Rails는 “자신의 작성 읽기"를 보장하며, delay
창 내에 있는 GET 또는 HEAD 요청을 작성기로 보냅니다. 기본적으로 지연 시간은 2초로 설정되어 있습니다. 데이터베이스 인프라에 따라 이 값을 변경해야 합니다. Rails는 지연 창 내에서 다른 사용자의 "최근 작성 읽기"를 보장하지 않으며, 최근에 작성하지 않은 경우 GET 및 HEAD 요청을 복제본으로 보냅니다.
Rails의 자동 연결 전환은 상대적으로 기본적이며 의도적으로 많은 기능을 하지 않습니다. 목표는 애플리케이션 개발자가 충분히 사용자 정의할 수 있는 자동 연결 전환 시스템을 보여주는 것입니다.
Rails의 설정을 통해 전환 방식과 기준 매개변수를 쉽게 변경할 수 있습니다. 예를 들어 세션 대신 쿠키를 사용하여 연결을 전환하려면 다음과 같은 사용자 정의 클래스를 작성할 수 있습니다:
class MyCookieResolver < ActiveRecord::Middleware::DatabaseSelector::Resolver def self.call(request) new(request.cookies) end def initialize(cookies) @cookies = cookies end attr_reader :cookies def last_write_timestamp self.class.convert_timestamp_to_time(cookies[:last_write]) end def update_last_write_timestamp cookies[:last_write] = self.class.convert_time_to_timestamp(Time.now) end def save(response) end end
그리고 미들웨어에 전달합니다:
config.active_record.database_selector = { delay: 2.seconds } config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver config.active_record.database_resolver_context = MyCookieResolver
수동 연결 전환 사용
자동 연결 전환으로는 충분하지 않은 경우가 있습니다. 예를 들어 특정 요청에 대해 항상 복제본으로 보내고 싶지만, POST 요청 경로에 있는 경우에도 그렇게 하고 싶을 수 있습니다.
이를 위해 Rails는 필요한 연결로 전환할 수 있는 connected_to
메서드를 제공합니다.
ActiveRecord::Base.connected_to(role: :reading) do # 이 블록의 모든 코드는 읽기 역할에 연결됩니다. end
connected_to
호출의 "역할"은 해당 연결 핸들러(또는 역할)에 연결된 연결을 조회합니다. reading
연결 핸들러에는 connects_to
를 통해 reading
역할 이름으로 연결된 모든 연결이 포함됩니다.
connected_to
로 역할을 전환하면 기존 연결을 조회하고 연결 사양 이름을 사용하여 전환합니다. 따라서 connected_to(role: :nonexistent)
와 같이 알 수 없는 역할을 전달하면 ActiveRecord::ConnectionNotEstablished (No connection pool for 'ActiveRecord::Base' found for the 'nonexistent' role.)
와 같은 오류가 발생합니다.
쿼리가 읽기 전용인지 확실히 하려면 prevent_writes: true
를 전달하세요. 이렇게 하면 쓰기 작업으로 보이는 쿼리가 데이터베이스로 전송되는 것을 방지합니다. 복제본 데이터베이스를 읽기 전용 모드로 구성해야 합니다.
ActiveRecord::Base.connected_to(role: :reading, prevent_writes: true) do # Rails는 각 쿼리가 읽기 쿼리인지 확인합니다. end
수평 분할
수평 분할은 각 데이터베이스 서버의 행 수를 줄이기 위해 데이터베이스를 분할하는 것이지만, "샤드” 간에 동일한 스키마를 유지합니다. 이를 “다중 테넌트” 분할이라고도 합니다.
Rails의 수평 분할 API는 Rails 6.0부터 존재한 다중 데이터베이스/수직 분할 API와 유사합니다.
샤드는 다음과 같이 3단계 구성에 선언됩니다:
production: primary: database: my_primary_database adapter: mysql2 primary_replica: database: my_primary_database adapter: mysql2 replica: true primary_shard_one: database: my_primary_shard_one adapter: mysql2 migrations_paths: db/migrate_shards primary_shard_one_replica: database: my_primary_shard_one adapter: mysql2 replica: true primary_shard_two: database: my_primary_shard_two adapter: mysql2 migrations_paths: db/migrate_shards primary_shard_two_replica: database: my_primary_shard_two adapter: mysql2 replica: true
모델은 그런 다음 connects_to
API를 통해 shards
키로 연결됩니다:
class ApplicationRecord < ActiveRecord::Base primary_abstract_class connects_to database: { writing: :primary, reading: :primary_replica } end class ShardRecord < ApplicationRecord self.abstract_class = true connects_to shards: { shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }, shard_two: { writing: :primary_shard_two, reading: :primary_shard_two_replica } } end
샤드를 사용하는 경우 migrations_paths
와 schema_dump
가 모든 샤드에 대해 변경되지 않도록 해야 합니다. 마이그레이션을 생성할 때 --database
옵션을 전달하고 샤드 이름 중 하나를 사용할 수 있습니다. 경로가 모두 동일하므로 어떤 것을 선택해도 상관없습니다.
$ bin/rails g scaffold Dog name:string --database primary_shard_one
그런 다음 모델은 수동으로 connected_to
API를 통해 샤드를 전환할 수 있습니다. 샤드를 사용하는 경우 role
과 shard
를 모두 전달해야 합니다:
ActiveRecord::Base.connected_to(role: :writing, shard: :default) do @id = Person.create! # :default 샤드에 레코드 생성 end ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do Person.find(@id) # 레코드를 찾을 수 없음, :default 샤드에 생성되었기 때문 end
수평 분할 API는 읽기 복제본도 지원합니다. connected_to
API를 사용하여 역할과 샤드를 전환할 수 있습니다.
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do Person.first # shard one의 읽기 복제본에서 레코드 조회 end
자동 샤드 전환 활성화
애플리케이션은 제공된 미들웨어를 사용하여 요청별로 자동으로 샤드를 전환할 수 있습니다.
ShardSelector
미들웨어는 자동으로 샤드를 전환하는 프레임워크를 제공합니다. Rails는 어떤 샤드로 전환할지 결정하는 기본 프레임워크를 제공하며, 필요한 경우 애플리케이션에서 사용자 정의 전략을 작성할 수 있습니다.
ShardSelector
는 미들웨어 동작을 변경하는 데 사용할 수 있는 옵션 집합(현재 lock
만 지원)을 받습니다. lock
은 기본적으로 true이며, 블록 내부에서 샤드 전환을 금지합니다. lock
이 false인 경우 샤드 전환이 허용됩니다. 테넌트 기반 분할의 경우 lock
은 항상 true여야 합니다.
데이터베이스 선택기와 동일한 생성기를 사용하여 자동 샤드 전환을 위한 파일을 생성할 수 있습니다:
$ bin/rails g active_record:multi_db
그런 다음 파일에서 다음 내용의 주석을 제거합니다:
Rails.application.configure do config.active_record.shard_selector = { lock: true } config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard } end
애플리케이션은 해결기 코드를 제공해야 하며, 이는 애플리케이션별 모델에 따라 달라집니다. 다음과 같은 해결기 예시가 있습니다:
config.active_record.shard_resolver = ->(request) { subdomain = request.subdomain tenant = Tenant.find_by_subdomain!(subdomain) tenant.shard }
세부적인 데이터베이스 연결 전환
Rails 6.1에서는 전체 데이터베이스 대신 하나의 데이터베이스에 대해서만 연결을 전환할 수 있습니다.
세부적인 데이터베이스 연결 전환을 통해 모든 데이터베이스 전역적으로 영향을 미치지 않고 추상 연결 클래스에서 연결을 전환할 수 있습니다. 이는 AnimalsRecord
쿼리를 복제본에서 읽도록 전환하면서 ApplicationRecord
쿼리가 기본 데이터베이스로 가도록 하는 데 유용합니다.
AnimalsRecord.connected_to(role: :reading) do Dog.first # animals_replica에서 읽음 Person.first # primary에서 읽음 end
샤드에 대해서도 세부적으로 연결을 전환할 수 있습니다.
AnimalsRecord.connected_to(role: :reading, shard: :shard_one) do Dog.first # shard_one_replica에서 네, 번역을 계속하겠습니다. ```ruby AnimalsRecord.connected_to(role: :reading, shard: :shard_one) do Dog.first # shard_one_replica에서 읽음. shard_one_replica 연결이 없는 경우 ConnectionNotEstablished 오류가 발생합니다. Person.first # primary 작성기에서 읽음 end
기본 데이터베이스 클러스터만 전환하려면 ApplicationRecord
를 사용합니다:
ApplicationRecord.connected_to(role: :reading, shard: :shard_one) do Person.first # primary_shard_one_replica에서 읽음 Dog.first # animals_primary에서 읽음 end
ActiveRecord::Base.connected_to
는 전역적으로 연결을 전환할 수 있는 기능을 유지합니다.
데이터베이스 간 조인이 있는 연관 관계 처리
Rails 7.0+ 버전부터 Active Record에는 데이터베이스 간 조인을 처리하는 옵션이 있습니다. 데이터베이스 간 조인이 필요한 has many through 또는 has one through 연관 관계의 경우 disable_joins: true
옵션을 전달하면 2개 이상의 쿼리를 실행하도록 할 수 있습니다.
예를 들어:
class Dog < AnimalsRecord has_many :treats, through: :humans, disable_joins: true has_many :humans has_one :home has_one :yard, through: :home, disable_joins: true end class Home belongs_to :dog has_one :yard end class Yard belongs_to :home end
이전에는 @dog.treats
를 호출할 때 disable_joins
를 사용하지 않거나 @dog.yard
를 호출할 때 disable_joins
를 사용하지 않으면 데이터베이스 간 조인을 처리할 수 없어 오류가 발생했습니다. disable_joins
옵션을 사용하면 클러스터 간 조인을 시도하지 않도록 여러 개의 선택 쿼리를 생성합니다. 위의 연관 관계에 대해 @dog.treats
는 다음과 같은 SQL을 생성합니다:
SELECT "humans"."id" FROM "humans" WHERE "humans"."dog_id" = ? [["dog_id", 1]] SELECT "treats".* FROM "treats" WHERE "treats"."human_id" IN (?, ?, ?) [["human_id", 1], ["human_id", 2], ["human_id", 3]]
그리고 @dog.yard
는 다음과 같은 SQL을 생성합니다:
SELECT "home"."id" FROM "homes" WHERE "homes"."dog_id" = ? [["dog_id", 1]] SELECT "yards".* FROM "yards" WHERE "yards"."home_id" = ? [["home_id", 1]]
이 옵션과 관련된 중요한 사항은 다음과 같습니다:
- 이제 조인 대신 두 개 이상의 쿼리가 실행되므로 성능 문제가 발생할 수 있습니다.
humans
에 대한 선택 결과가 많은 ID를 반환하면treats
에 대한 선택 쿼리에 너무 많은 ID가 전송될 수 있습니다. - 더 이상 조인을 수행하지 않으므로 정렬 또는 제한이 있는 쿼리는 메모리 내에서 정렬됩니다. 한 테이블의 순서를 다른 테이블에 적용할 수 없기 때문입니다.
- 이 설정은 조인을 비활성화하려는 모든 연관 관계에 추가해야 합니다. Rails는 지연 로딩 때문에 이를 추측할 수 없습니다.
@dog.treats
에서treats
를 로드하려면 Rails가 이미 생성해야 할 SQL을 알고 있어야 합니다.
스키마 캐싱
각 데이터베이스에 대한 스키마 캐시를 로드하려면 각 데이터베이스 구성에 schema_cache_path
를 설정하고 애플리케이션 구성에 config.active_record.lazily_load_schema_cache = true
를 설정해야 합니다. 데이터베이스 연결이 설정될 때 캐시가 지연 로드됩니다.
주의 사항
복제본 로드 밸런싱
Rails는 복제본의 자동 로드 밸런싱도 지원하지 않습니다. 이는 인프라에 매우 의존적입니다. 향후 기본적이고 원시적인 로드 밸런싱을 구현할 수 있지만, 규모가 큰 애플리케이션의 경우 이는 Rails 외부에서 처리해야 합니다.