リロード時のExecutionContext破損を修正

rails/rails

この変更により、ActiveSupport::Reloader#reload! 実行時に ActiveSupport::ExecutionContext が破損する問題が修正されました。この問題は config.enable_reloading = true を設定したテスト環境で発生し、ActiveSupport::CurrentAttributes から NoMethodError が発生する原因となっていました。

背景

この問題は #55247 で導入された、テスト環境での ExecutionContext のネスト機能によるリグレッションです。ExecutionContext::Record@store が常に Hash であることを前提としていますが、リロード後に nil になるケースがありました。

Springなどの開発ツールでは config.enable_reloading = true の設定が推奨されており、この設定下でテストを実行すると問題が顕在化します。以下のような操作シーケンスで再現します:

class Current < ActiveSupport::CurrentAttributes
  attribute :trace_id
end

Rails.application.reloader.reload!

# NoMethodError: undefined method '[]' for nil
Current.trace_id

問題の根本原因は、リロード時のフック実行順序にあります。app.executor.to_runpush されたコンテキストが、app.reloader.before_class_unloadclear され、その後 app.executor.to_completepop される際にスタックが空になっていました。

技術的な変更

本PRでは、リロード時のコンテキストリセット方法を変更しています。新たに ExecutionContext.flush メソッドが追加され、スタック深度を保持したままコンテキスト内容をクリアします。

変更前:

app.reloader.before_class_unload do
  ActiveSupport::CurrentAttributes.clear_all
  ActiveSupport::ExecutionContext.clear
  ActiveSupport.event_reporter.clear_context
end

変更後:

app.reloader.before_class_unload do
  ActiveSupport::CurrentAttributes.clear_all
  ActiveSupport::ExecutionContext.flush
  ActiveSupport.event_reporter.clear_context
end

flush メソッドの実装では、スタックの各要素を空の Hash で初期化し、@store@current_attributes_instances もリセットします:

def flush
  @stack = Array.new(@stack.size) { {} }
  @store = {}
  @current_attributes_instances = {}
  self
end

clear がスタック自体を破棄していたのに対し、flush はスタックサイズを維持します。これにより、push -> flush -> pop のシーケンスでも正しく動作するようになりました。

設計判断

スタック深度の保持 という設計が採用されました。

clear を使い続ける代わりに flush を導入した理由は、テスト環境でのネストされた実行コンテキストとの互換性です。#55247 でテストケース内でのコンテキストネストが可能になりましたが、リロード時にスタックが完全にクリアされると、外側のコンテキストに戻れなくなります。

flush は各スタックレベルの内容をクリアしながらも構造を保持するため、pop が正しく前の状態を復元できます。この設計により、テストの実行コンテキストとコントローラ/ジョブの実行コンテキストを分離するという #55247 の目的を損なうことなく、リロード機能との両立を実現しています。

テストでは push -> flush -> pop のシーケンスが正常に動作することを検証しており、リロード後もコンテキストが有効な状態を保つことが保証されています。

まとめ

本PRは、リロード機能とネストされた実行コンテキストの両立を可能にする修正です。ExecutionContext.clearflush に置き換えることで、スタック構造を保持したままコンテキストをリセットし、Springなどの開発ツールでの config.enable_reloading = true 設定との互換性を確保しています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

「総論→各論→結論」の構成が明確です。リード文で要旨を述べ、背景、技術詳細、設計判断、まとめが論理的に配置されています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライトやGitHubのPR番号リンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

内容はRailsの内部実装に関するもので、専門知識を持つエンジニアという対象読者に適した技術レベルで書かれています。

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

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

各セクション・パラグラフの構成が適切です。トピックセンテンスが段落の冒頭にあり、1段落1トピックの原則が守られているため、可読性が非常に高いです。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiffの内容と正確に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`ExecutionContext`、`Reloader`、`flush`といった技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

バグの根本原因や`clear`と`flush`の違いに関する説明は、PR情報と照合した結果、技術的に正確で論理的です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのタイトル、Description、Diffの内容で裏付けられており、ハルシネーション(捏造)は見られません。

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

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

PR番号(#56889, #55247)などの数値や固有名詞はすべて正確です。

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

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

記事のタイトル「リロード時のExecutionContext破損を修正」は、PRのタイトルと内容を正確に反映しています。

外部知識の正確性 ✓ PASS

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

PRで言及されていないバージョン情報やサポート状況などの外部知識は含まれておらず、信頼性が高いです。

時間表現の正確性 ✓ PASS

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

「〜が原因となっていました」などの時間表現は、PRの文脈と一致しており正確です。