Rails 6 を使用したテスト環境で複数のデータベース/レプリカが機能しない

概要

Distribution_reads からネイティブの Rails 6 の複数データベースのサポートに移行しようとしているときに、奇妙な動作に遭遇しました。それがバグなのか設定の問題なのかはまだわかりません。

// database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: 10
  reaping_frequency: 10
  timeout: 5000
  username: my_username
  password: my_password
  database: my_database
  schema_search_path: 'public,utils'

development: &development
  primary:
    <<: *default
    database: my_database
    host: 'localhost'
  primary_replica:
    <<: *default
    database: my_database
    host: 'localhost'
    replica: true
  third_replica:
    <<: *default
    database: my_database
    host: 'localhost'
    replica: true

test: &test
  primary:
    <<: *default
    database: 'my_database_test'
    host: 'localhost'
  primary_replica:
    <<: *default
    database: 'my_database_test'
    host:'localhost'
    replica: true
  third_replica:
    <<: *default
    database: 'my_database_test'
    host: 'localhost'
    replica: true

このコードベースのすべてのレコードに接続する ApplicationRecord も変更しました。

# application_record.rb
connects_to shards: {
  default: { writing: :primary, reading: :primary_replica },
  admin: { writing: :primary, reading: :third_replica }
}

開発中は、正しく動作します

> ActiveRecord::Base.connected_to(role: :reading, shard: :default, prevent_writes: true) do
  EventType.count
end
   (72.3ms)  SELECT COUNT(*) FROM "event_types" /*line:(pry):2:in `block in <main>'*/
=> 2

これは正しいレコード数です。テストを実行して同じ行を使用すると、ラッパーの外側にある EventType.count も正しい数をカウントしますが、どのモデルでもどのレコードをチェックしていても、connected_to の結果は 0 になります。

> EventType.count
=> 1
> ActiveRecord::Base.connected_to(role: :reading, shard: :default, prevent_writes: true) do
  EventType.count
end
=> 0

開発/テストのレプリカは実際には同じデータベースであるため、私の場合は 0 を取得することはできないはずです。

書き込みロールを使用する場合は機能しますが、

> ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
  EventType.count
end
=> 1

shards: の部分は、database: を介した簡易バージョンでも壊れ、 third_replica がないため、重要ではないようです。

この Rails 機能について何か足りないものはありますか?これと互換性のないテストに関連するものはありますか?これに関連するドキュメントは見つかりませんでした。

解決策

結局、問題はテスト設定全体を通して発生したトランザクションに起因していました。

DatabaseCleaner.strategy = :transaction

そして、それが私たちのコードベース全体ではるかに大きな問題であるという証拠を示しました。クエリをトランザクションにラップし、connected_to を使用すると、基本的にそのトランザクションから抜け出すことになり、この特定のケースでは多くの一貫性の問題が発生します。これは純粋に論理的ですが、これをスタックに追加すると、最初に存在したものと互換性がなくなる可能性があります。

結論としては、トランザクションを使用するときとリードレプリカを扱うときは注意する必要があります。