`CurrentAttributes#clear_all` でのHash反復中追加エラーを修正

rails/rails

ActiveSupport::CurrentAttributes.clear_all の実行中に別の CurrentAttributes サブクラスが初期化されると RuntimeError: can't add a new key into hash during iteration が発生していたバグが修正されました。修正は1行の変更で、each_value から values.each への置き換えにより、反復前にHashのスナップショットを取ることで解決しています。

背景

clear_all はテスト間のステート漏洩を防ぐため、すべての CurrentAttributes インスタンスをリセットする内部メソッドです。しかし resets コールバックの中で別の CurrentAttributes サブクラスのアトリビュートに初めてアクセスすると、その場でインスタンスが current_instances ハッシュに追加され、反復処理中のHashへの新規キー追加として例外が発生していました。

この問題は、コミット d43dc9a で導入された push / pop ロジックによる ExecutionContext の改善の一部として修正されていましたが、本PRはその修正を現行ブランチにバックポートしたものです。

技術的な変更

current_instances ハッシュを反復しながらその中身が変更されないよう、反復開始前に値のスナップショットを取る方式に変更しました。

変更前:

def reset_all # :nodoc:
  current_instances.each_value(&:reset)
end

変更後:

def reset_all # :nodoc:
  current_instances.values.each(&:reset)
end

each_value はHashを直接参照しながら反復するため、コールバック内で新しいサブクラスインスタンスが current_instances に追加されると即座に例外が発生します。一方 values はHash全体の値を配列としてコピーした後に each で反復するため、コールバック実行中に current_instances が変更されても安全です。

テストでは、resets ブロック内で別の CurrentAttributes サブクラスのアトリビュート(default: -> { SecureRandom.uuid } を持つ auxiliary.uuid)に初回アクセスするシナリオが追加され、assert_nothing_raised により例外が発生しないことを確認しています。

設計判断

values.each による防御的コピー という最小限のアプローチが選択されました。

コールバックが current_instances を変更しうるという前提のもと、反復対象を事前に確定させることで問題を解消しています。コールバック実行中に新たに追加されたインスタンスは今回の reset_all サイクルではリセットされませんが、次のサイクルで処理されます。これは resets コールバック内での遅延初期化という用途では許容できる挙動であり、余分なロックや複雑な制御フローを導入せずに済む判断です。

まとめ

each_value から values.each への1行の変更が、resets コールバック内での遅延初期化という実際のユースケースで発生していたクラッシュを防ぎます。Hashの反復中に要素が追加されうる場面ではスナップショットを取るという、Rubyでの定番の防御的パターンを CurrentAttributes のライフサイクル管理に適用した修正といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
212136f9

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

「総論→各論→結論」の構成が明確です。リード文で要旨を述べ、背景、技術詳細、設計判断で多角的に掘り下げ、最後にまとめで意義を述べるという、模範的な構成になっています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きのシンタックスハイライト(```ruby:ファイルパス)や、コミットID、PR番号のリンク記法がガイドラインに沿って正しく使用されています。

対象読者への適合性 ✓ PASS

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

内容は`ActiveSupport::CurrentAttributes`の内部実装やRubyのハッシュ反復処理の特性に触れており、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

各セクションの冒頭で要旨を述べ、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に高い可読性を実現しています。

Diff内容との照合 ✓ PASS

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

Diff内のコード変更(`each_value`から`values.each`へ)とファイル名を正確に引用しています。また、追加されたテストケースの意図も的確に要約できています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`CurrentAttributes`, `resets`コールバック, `each_value`, `values.each`など、関連する技術用語を正確かつ文脈に即して使用しています。

説明の技術的正確性 ✓ PASS

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

`each_value`(直接反復)と`values.each`(スナップショット反復)の挙動の違いに関する説明は技術的に正確であり、なぜこの変更でバグが修正されるのかを論理的に解説しています。

事実の突合 ✓ PASS

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

「バックポートである」という背景、修正対象のランタイムエラー、参照コミットなど、記事内の主張はすべてPRのDescriptionやDiff内容で裏付けられており、ハルシネーションは一切見られません。

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

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

PR番号(#57200)やコミットID(d43dc9a)などの数値・固有名詞はすべて正確に記載されています。

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

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

記事のタイトルは、PRのタイトル(発生したエラー)と修正内容を的確に要約しており、内容との整合性が取れています。

外部知識の正確性 ✓ PASS

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

PRで言及されていない外部のバージョン情報やリリース予定などを追記しておらず、提供された情報源に忠実な内容となっています。

時間表現の正確性 ✓ PASS

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

「発生していたバグが修正されました」といった時間表現は、PRの内容と一致しており、事実を正確に伝えています。