[rails/solid_queue] Rails 8.2の`enqueue_after_transaction_commit`再導入に伴うテスト適応
背景
Rails 8.2でenqueue_after_transaction_commit設定が再導入されました(rails/rails#55788)。この設定はRailsバージョンを跨いで異なる挙動を示してきたため、Solid Queueのテストコードをこれらの変遷に対応させる必要がありました。
設定の変遷:
- Rails 7.1: メソッド自体が存在しない(遅延エンキューなし)
- Rails 7.2: :default(アダプターのメソッドに委譲、Solid Queueではtrueを返す)
- Rails 8.0-8.1: false(遅延エンキューなし、設定は非推奨化)
- Rails 8.2: true(遅延エンキューがデフォルトで有効)
この変更により、トランザクション内でのジョブエンキューの挙動が変わるため、テストの条件分岐を適切に調整する必要があります。
主な変更内容
1. テストマトリクスの拡張
Rails mainブランチ(将来の8.2)に対するテストカバレッジを強化するため、Ruby 3.2の組み合わせを追加しました。
- ruby-version: "3.1"
gemfile: rails_main
- ruby-version: "3.2"
gemfile: rails_main
2. SQLite3の即時トランザクション処理の整理
Rails 8.0以降では即時トランザクションが標準サポートされたため、バージョンチェックを追加してモンキーパッチを条件付きで適用するように変更しました。
module SqliteImmediateTransactions
def begin_db_transaction
log("begin immediate transaction", "TRANSACTION") do
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
conn.transaction(:immediate)
end
end
end
end
ActiveSupport.on_load :active_record do
if defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
# Rails 8.0以降は即時トランザクションが組み込み済み
if Rails::VERSION::MAJOR < 8
ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SqliteImmediateTransactions
end
ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SQLite3Configuration
end
end
3. トランザクションテストのスキップ条件の改善
従来はRails 7.2のみをハードコードでスキップしていましたが、enqueue_after_transaction_commitの実際の設定値をチェックする汎用的な条件に変更しました。
変更前:
# Rails 7.2のみをスキップ
skip if Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR == 2
変更後:
# enqueue_after_transaction_commitが有効な場合はスキップ
skip if ActiveJob::Base.respond_to?(:enqueue_after_transaction_commit) &&
[ true, :default ].include?(ActiveJob::Base.enqueue_after_transaction_commit)
この変更により、Rails 7.2と8.2の両方で適切にテストがスキップされます。:defaultは実質的にtrueとして機能するため、両方の値をチェックしています。
4. 競合制御テストのタイミング調整
遅延エンキューが有効な場合、ジョブのエンキュータイミングが変わるため、テスト内にsleep(0.1)を追加してジョブが確実にエンキューされるまで待機するように修正しました。
test "discard jobs when concurrency limit is reached with on_conflict: :discard" do
job1 = DiscardableUpdateResultJob.perform_later(@result, name: "1", pause: 3)
sleep(0.1) # ジョブのエンキューを待機
# 同時実行数制限により破棄されるべき
job2 = DiscardableUpdateResultJob.perform_later(@result, name: "2")
job3 = DiscardableUpdateResultJob.perform_later(@result, name: "3")
# ...
end
技術的な影響
enqueue_after_transaction_commitが有効な場合、ジョブはトランザクションコミット後まで実際のエンキューが遅延されます。これにより:
- トランザクションのロールバック: ロールバック時にジョブがエンキューされない保証が得られる
- タイミングの変化: テストコード内でのジョブの可視性が変わるため、適切な待機処理が必要
- 競合制御: 同時実行数制限などの機能が期待通りに動作するためには、ジョブが実際にエンキューされるタイミングを考慮する必要がある
この変更により、Solid QueueはRails 8.2の新しい動作に対応しつつ、過去のバージョンとの互換性も維持できるようになりました。