Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails 7: シャードの意図しないスワップを防止する機能が追加(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

関連: 週刊Railsウォッチ20211108 ActiveRecord::Base.prohibit_shard_swappingを追加

この改修は、7-0-stableブランチに含まれています。

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 7: connected_toでロールの指定が必須でなくなった(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。