例外を位置引数で渡すことで Continuable の例外記録が正しくなる

rails/rails

ActiveJob の Continuable ジョブは、途中で回復可能なエラーが発生した場合に continue から再開されますが、エラー情報の記録方法が分岐ごとに不一致でした。本 PR は resume_job を位置引数で呼び出すよう統一し、例外メッセージをキーとした正しい統計が蓄積されるようにします。

背景

続行可能ジョブが StandardError を捕捉した際、従来は resume_job(exception: e) とキーワード形式で呼び出していました。resume_job は位置引数のみを受け取るシグネチャであるため、ハッシュ { exception: e } がそのまま exception パラメータに渡り、executions_for がハッシュ文字列をキーとして記録していました。その結果、例外メッセージではなく "{exception: #<StandardError: …>}"exception_executions に保存され、Interrupt 分岐とキーが食い違う状態が発生していました。

この不整合はテストでも露呈しており、エラーレジューム後の統計が正しく集計できないという実用的な問題につながっていました。

技術的な変更

continue メソッドの例外処理統一

activejob/lib/active_job/continuable.rbcontinue メソッド内で、StandardError 捕捉時に 位置引数resume_job を呼び出すよう変更しました。

変更前:

rescue StandardError => e
  if resume_errors_after_advancing? && continuation.advanced?
    resume_job(exception: e)   # キーワード引数形式
  else
    raise
  end

変更後:

rescue StandardError => e
  if resume_errors_after_advancing? && continuation.advanced?
    resume_job(e)              # 位置引数形式
  else
    raise
  end

この一行の差し替えにより、resume_job が期待通りの exception オブジェクトを受け取り、executions_for が例外メッセージ文字列をキーとして正しく記録できるようになります。

テストの追加で挙動を検証

activejob/test/cases/continuation_test.rb に新たにテストケースを追加し、エラー後に exception_executions{ "Cursor error" => 1 } という形で蓄積されることを確認しました。

test "records the exception message as the exception_executions key when resuming after an error" do
  IteratingRecord.records = [ 123, 432, 6565, 3243, 234, 13, 22 ].map { |i| IteratingRecord.new(i, "item_#{i}") }

  IteratingJob.perform_later(raise_when_cursor: 433)

  assert_enqueued_jobs 1, only: IteratingJob do
    perform_enqueued_jobs
  end

  job = queue_adapter.enqueued_jobs.first
  assert_equal({ "Cursor error" => 1 }, job["exception_executions"])
end

テストはジョブがキューに入った後に実行され、exception_executions のキーが例外メッセージそのものになることを明示的に検証しています。

設計判断

resume_job のシグネチャは 位置引数 exception のみで定義されており、キーワード引数は意図されていませんでした。そのため、既存の Interrupt 分岐と同様に位置引数で呼び出す方針が採られました。PR の議論では新しいキーワードパラメータ exception: を導入する案も検討されましたが、シグネチャの変更は下位互換性への影響が大きく、既存の呼び出し側に不要なリファクタリングを強いる可能性があったため、最小限の変更で挙動統一 を選択しました。

この判断により、コードベース全体で resume_job の呼び出し方が一貫し、例外記録ロジックがシンプルかつ予測可能になります。

まとめ

resume_job を位置引数で呼び出す統一により、Continuable ジョブのエラー再開時に例外メッセージが正しく exception_executions に記録されるようになりました。追加テストで挙動を裏付け、既存のインターフェースを保ちながらバグを根本的に解消した点が本変更の意義です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
bd8f9b23

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的変更、設計判断(任意)とまとめの各セクションが揃っており、総論→各論→結論の構成が明確です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライトが正しい形式で使用され、PRリンクも正しい Markdown 記法で記載されています。

対象読者への適合性 ✓ PASS

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

対象は Rails の内部実装に詳しいエンジニア向けで、過度な初心者向け解説はなく適切です。

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

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

各セクションは総論・各論・結論の段落構成で、トピックセンテンスが先頭にあり、1段落1トピック・6文以内に収まっています。段落間は空行で区切られています。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供された Diff と完全に一致しており、変更点を正確に反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用された技術用語は PR 内容と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

技術的説明は PR の記述と合致しており、因果関係や根拠が明示されています。

事実の突合 ✓ PASS

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

全ての主張は PR タイトル、説明、Diff、テストコードで裏付けられており、ハルシネーションはありません。

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

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

数値や固有名詞(PR 番号、例外メッセージ等)は正確に記載されています。

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

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

日本語タイトルは PR の内容を正確に要約しており、一致しています。

外部知識の正確性 ✓ PASS

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

外部知識(バージョンやリリース情報等)は含まれておらず、PR に基づく記述のみです。

時間表現の正確性 ✓ PASS

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

時間表現の記述はなく、PR の記載と矛盾する箇所もありません。