Rails 7: シャードの意図しないスワップを防止する機能が追加(翻訳)
Railsでは、複数のデータベースに接続する方法のひとつとしてシャードを提供していますが、これはレプリケーションよりもずっと複雑です。各シャードは、アプリケーションのデータベース全体で垂直または水平のいずれかの形で継ぎ合わせられます。
垂直シャーディングを使うと、primaryデータベースで利用できるテーブルより多くのテーブルを読み書きできるようになります。水平シャーディングを使うと、定義されたリゾルバ(idがxより大きい、またはドメインがxに等しい)に基づいてprimaryデータベースのデータを水平分割できます。
データベースの設定方法は以下のようになります。
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
primary:
<<: *default
database: primary_database
primary_replica:
<<: *default
database: primary_database
replica: true
primary_shard_one:
<<: *default
database: primary_shard_one
migrations_paths: db/primary_shard_one_migrate
primary_shard_one_replica:
<<: *default
database: primary_shard_one
replica: true
primary_shard_two:
<<: *default
database: primary_shard_two
primary_shard_two_replica:
<<: *default
database: primary_shard_two
replica: true
ここでは以下のように、ApplicationRecordクラスで水平シャーディングを設定する例を用います。
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
connects_to shards: {
default: { writing: :primary, reading: :primary_replica },
shard_two: { writing: :primary_shard_two, reading: :primary_shard_two_replica }
}
end
Railsのシャーディングでは、垂直と水平のどちらのシャーディングについても手動または自動でシャードを切り替えられます。
Active Recordの各コネクション(本質的にはスレッド)には、現在アクセス中のデータベースに関する情報が含まれます。コネクション情報は以下に保存されます。
> ActiveRecord::Base.connection_db_config
=> #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fc1831657f0 @env_name="development", @name="primary", @configuration_hash={:adapter=>"postgresql", :encoding=>"unicode", :pool=>5, :database=>"primary_database"}>
変更前
Active Recordは、シャードを手軽にスワップアウトできるconnect_to
を提供しています。
ActiveRecord::Base.connected_to(role: :reading, shard: :default) do
puts ActiveRecord::Base.connection_db_config.name
Blog.count
end
primary_replica
Blog Count (3.3ms) SELECT COUNT(*) FROM "blogs"
=> 3
シャードをshard_two
にスワップしましょう。
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_two) do
puts ActiveRecord::Base.connection_db_config.name
Blog.count
end
primary_shard_two_replica
Blog Count (3.3ms) SELECT COUNT(*) FROM "blogs"
=> 0
このシャードがまだ書き込まれていないので、ブログは0件になってしまいました。
変更後
ActiveRecord::Base.prohibit_shard_swapping
を使って、ブロック内でのシャードを変更する試みを防止できるようになりました(#43485)。シャーディングされたデータベースをリクエスト全体のライフサイクルで使う場合、データベースのシャードが意図せず変更されないことが望ましいことがよくあります。
このオプションはスレッドセーフでもあります。
実際に動かしてみましょう。
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_two) do
puts ActiveRecord::Base.connection_db_config.name
puts Blog.count
ActiveRecord::Base.prohibit_shard_swapping do
ActiveRecord::Base.connected_to(role: :reading, shard: :default) do
puts ActiveRecord::Base.connection_db_config.name
puts Blog.count
end
end
end
primary_shard_two_replica
Blog Count (1.5ms) SELECT COUNT(*) FROM "blogs"
0
Traceback (most recent call last):
3: from (irb):84
2: from (irb):88:in `block in irb_binding'
1: from (irb):89:in `block (2 levels) in irb_binding'
ArgumentError (cannot swap `shard` while shard swapping is prohibited.)
irb(main):095:0>
概要
原著者の許諾を得て翻訳・公開いたします。
関連: 週刊Railsウォッチ20211108
ActiveRecord::Base.prohibit_shard_swapping
を追加この改修は、7-0-stableブランチに含まれています。