🐢
Rails + PostgreSQL でトランザクションを書くときに、トランザクションの外で行ったデータ操作の影響
これは何?
- Rails + PostgreSQLでトランザクションを書くとき、トランザクションの内外でデータ操作の影響がどう変わるかの備忘録
- 環境
Rails 8PostgreSQL 17
トランザクションの中でのデータ変更について
トランザクションの中で発生したupdate, create, destroyなどのデータ変更は、分離レベルに関係なく、コミットされるまでトランザクションの外には反映されません。
トランザクションの外でのデータ変更について
オプションなしのトランザクションの場合
ActiveRecord::Base.transactionをisolationオプション指定なしで実行すると、分離レベルは PostgreSQLではデフォルトのRead Commitedとなります。この状態だと、トランザクションの外でcommitされたデータがトランザクション内に反映されます。
たとえば以下のトランザクションでbinding.bした箇所で、別のモデルをcreate,update,destroyしてコミットすると、コミット結果をトランザクション内で見ることができます。
def test_transaction
Supplier.transaction do
binding.b
update!(name: "updated_name")
end
end
PostgreSQLのデフォルトより高レベルの分離オプションを指定したトランザクションの場合
ActiveRecord::Base.transactionを、PostgreSQLのデフォルトより高レベルのisolationオプション指定あり(:repeatable_read or :serializable)で実行すると、トランザクションの外でcommitされたデータはトランザクション内に反映されません。上記のトランザクションを以下のように変えると、別のモデルのデータはトランザクション開始時点のデータを参照します。
def test_transaction
- Supplier.transaction do
+ Supplier.transaction(isolation: :repeatable_read) do
binding.b
update!(name: "updated_name")
end
end
使い分けのユースケース
意図しないロールバックが発生したときに適切にrescueせず、500エラーがユーザーに出てしまう場合は対策したほうがいいかも
参考
Discussion