`enqueue_after_transaction_commit` のバージョン変遷に対応したテスト修正

rails/solid_queue

Rails 7.1から8.2にかけて enqueue_after_transaction_commit 設定が複数回変更されたことを受け、Solid Queueのテストコードをバージョン横断で正しく動作するよう修正しました。

背景

enqueue_after_transaction_commit は、トランザクション内でエンキューされたジョブをトランザクションのコミット後に遅延実行するかどうかを制御する設定です。この設定はRailsのバージョンごとに異なる挙動を示してきました(rails/rails #55788 参照)。

各Railsバージョンにおける挙動は次の通りです:

  • Rails 7.1: メソッド自体が存在せず、遅延エンキューは行われない
  • Rails 7.2: :default(アダプターの実装に委譲。Solid Queueでは true を返す)
  • Rails 8.0〜8.1: false(遅延エンキューなし、設定は非推奨化)
  • Rails 8.2: true(遅延エンキューがデフォルトで有効化)

このような変遷により、従来のテストコードは「遅延エンキューが問題となるのはRails 7.2のみ」という前提で書かれていたため、Rails 8.2で再び true になったことで動作が崩れていました。

技術的な変更

スキップ条件のハードコードを廃止し、実行時の設定値を検査するよう変更されました。これにより、どのRailsバージョンでも正しくスキップ判定が行われます。

test/integration/concurrency_controls_test.rb および test/models/solid_queue/job_test.rb の2箇所で、バージョン番号による判定から設定値による判定へ変更されています。

変更前:

# Doesn't work with enqueue_after_transaction_commit? true on SolidQueueAdapter, but only Rails 7.2 uses this
skip if Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR == 2

変更後:

# Doesn't work when enqueue_after_transaction_commit is enabled
skip if ActiveJob::Base.respond_to?(:enqueue_after_transaction_commit) &&
        [ true, :default ].include?(ActiveJob::Base.enqueue_after_transaction_commit)

respond_to? でメソッドの存在確認(Rails 7.1対応)を行いつつ、値が true または :default の場合にスキップする構造になっています。:default を含めているのは、Solid QueueのアダプターがデフォルトでJOBの遅延エンキューを有効にするRails 7.2の挙動に対応するためです。

加えて、test/dummy/config/initializers/sqlite3.rb ではSQLiteのimmediate transactionに関するバージョン分岐も整理されています。

変更前:

def begin_db_transaction
  if Rails.gem_version < Gem::Version.new("8.2")
    log("begin immediate transaction", "TRANSACTION") do
      with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
        conn.transaction(:immediate)
      end
    end
  end
end

変更後:

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

モジュールのロード時の条件分岐も変更されています。

変更前:

ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SqliteImmediateTransactions

変更後:

# Rails 8.0+ has immediate transactions built-in
if Rails::VERSION::MAJOR < 8
  ActiveRecord::ConnectionAdapters::SQLite3Adapter.prepend SqliteImmediateTransactions
end

Rails 8.0以降はimmediate transactionが組み込まれているため、SqliteImmediateTransactions モジュールのprepend自体をRails 7系のみに限定しています。これによりメソッド内のバージョン分岐が不要になり、コードがシンプルになっています。

また、.github/workflows/main.yml にRuby 3.2 + rails_main の組み合わせがCIマトリクスに追加されており、Rails 8.2系での継続的な検証が行われます。

タイミング依存のテストに対しては、sleep(0.1) が2箇所追加され、並行処理における競合状態を緩和しています。

設計判断

バージョン番号ではなく設定値を直接検査する方式 が採用されました。

バージョン番号による条件分岐(Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR == 2)は、将来のバージョンで同じ挙動が再導入された際に漏れが生じるという脆弱性を持ちます。ActiveJob::Base.enqueue_after_transaction_commit の値を直接参照することで、実際の動作に基づいた判定が可能になり、Rails 8.2での true 再導入のような変化にも自動的に追従できます。

SQLiteのimmediate transaction設定においては、モジュールのprepend自体を条件付きにすることで、メソッドの実装をシンプルに保つ方針が選ばれています。

まとめ

バージョン番号のハードコードから設定値の動的検査への移行により、Solid Queueのテストスイートは将来のRailsバージョンでの挙動変化に対してより堅牢になりました。enqueue_after_transaction_commit のような設定が今後も変更される可能性がある中で、「バージョンではなく振る舞いで判定する」アプローチはメンテナンスコストを下げる実践的な設計といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Review Error for DiffDaily

Review Criteria:

記事構成 ✓ OK

Title, Context, Technical Detailの存在と明確さ

カスタムMarkdown構文 ✓ OK

シンタックスハイライト・GitHubリンク記法の正確性

対象読者への適合性 ✓ OK

エンジニア向けの適切な技術レベルと表現

パラグラフ・ライティング ✓ OK

トピックセンテンス・1段落1トピック・段落長

Diff内容との照合 ✓ OK

コードブロックとDiff内容の一致

技術用語の正確性 ✓ OK

技術用語の正確な使用

説明の技術的正確性 ✓ OK

技術的主張の正確性と論理性

事実の突合 ✓ OK

PR情報による主張の裏付け(ハルシネーション検出)

数値・固有名詞の確認 ✓ OK

PR番号・コミットID・バージョン等の正確性

タイトル・説明との一致 ✓ OK

記事タイトル・説明とPR内容の一致

外部知識の正確性 ✓ OK

PRに記載のない外部知識(LTS、サポート状況など)の不使用

時間表現の正確性 ✓ OK

時間表現がPR情報と一致しているか