filter_parametersの組み合わせ爆発を回避

rails/rails

Rails 8.1では、Active Record暗号化を使用する際に Rails.application.config.filter_parameters が指数関数的に増加する問題が修正されました。これにより、属性フィルタリングのパフォーマンスが大幅に改善されます。

背景

#55251 によって filter_attributes に追加された属性が自動的に config.filter_parameters にも追加されるようになりました。しかし、このアプローチには重大な副作用がありました。Active Record暗号化を使用すると、暗号化された属性がモデル名のプレフィックス付きで filter_attributes に追加されます。self.filter_attributes += [attr] というコードにより、既存の config.filter_parameters のすべてのエントリにもモデル名のプレフィックスが付けられてしまうという問題が発生しました。

30個のエントリを持つ config.filter_parameters と、Active Record暗号化を使用する30個のモデルがある場合、組み合わせにより1000個以上のエントリが生成されます。これらのエントリから生成される巨大な正規表現により、属性フィルタリングのパフォーマンスが著しく低下し、実環境では軽量なコントローラーが処理時間の95%を EventReporter のイベントフィルタリングに費やすケースも報告されました。

技術的な変更

activerecord/lib/active_record/core.rbfilter_attributes= メソッドが変更され、親クラスから継承された属性と新規追加された属性を区別するようになりました。

変更前:

def filter_attributes=(filter_attributes)
  @inspection_filter = nil
  @filter_attributes = filter_attributes

  FilterAttributeHandler.sensitive_attribute_was_declared(self, filter_attributes)
end

変更後:

def filter_attributes=(filter_attributes)
  @inspection_filter = nil
  previous = if @filter_attributes
    self.filter_attributes
  else
    Base.filter_attributes
  end
  changes = @filter_attributes = filter_attributes

  changes -= previous if previous
  FilterAttributeHandler.sensitive_attribute_was_declared(self, changes)
end

FilterAttributeHandler.sensitive_attribute_was_declared には、全体のリストではなく新規追加された属性(changes)のみが渡されるようになりました。previous 変数には、現在のクラスに @filter_attributes が設定されている場合はその値が、未設定の場合は Base.filter_attributes が格納されます。差分計算により、既に継承されている属性が重複して処理されることを防ぎます。

filter_attributes ゲッターメソッドも修正され、スーパークラスが Base を継承している場合のみ委譲するようになりました。

def filter_attributes
  if @filter_attributes.nil?
    if superclass <= Base
      superclass.filter_attributes
    else
      nil
    end
  else
    @filter_attributes
  end
end

設計判断

差分のみを通知する方式が採用されました。PR内では全体のリストを渡す方式も検討されましたが、継承チェーン上での重複処理を避けるため、新規追加分のみを FilterAttributeHandler に渡す設計が選ばれています。

テストコードの変更からも設計意図が読み取れます。以前は個々の属性の存在確認を行っていましたが、修正後は config.filter_parameters 全体が期待値と一致するかを検証するようになりました。

assert_equal ["generic_filtered", "credit_card.expires_at", "credit_card.digits"], Rails.application.config.filter_parameters

このテストは、generic_filtered という既存のエントリに対して、CreditCard モデルで追加された expires_atdigits のみがプレフィックス付きで追加されることを確認しています。ApplicationRecordBase のプレフィックスが付かないことも検証しており、継承チェーンでの重複が排除されていることが分かります。

まとめ

本PRは、Active Record暗号化使用時の config.filter_parameters の指数関数的増加を解消した変更です。継承された属性と新規属性を区別することで、組み合わせ爆発を防ぎ、実環境で報告されていた深刻なパフォーマンス劣化を根本から解決しています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事は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リンク記法の正確性

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

対象読者への適合性 ✓ PASS

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

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

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

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

各セクションが総論→各論の構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、可読性が高いです。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードは、提供されたDiffの内容とファイル名を含めて正確に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「filter_parameters」「組み合わせ爆発」「継承チェーン」など、PRの内容に沿った正確で適切な技術用語が使用されています。

説明の技術的正確性 ✓ PASS

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

パフォーマンス問題の原因と、差分計算による解決策についての説明は、Diffの内容と論理的に整合しており、技術的に正確です。

事実の突合 ✓ PASS

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

パフォーマンス劣化の具体的な状況や組み合わせ爆発の例など、記事内のすべての主張がPRのDescriptionやDiffの内容によって裏付けられています。

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

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

PR番号(#55251, #56759)や、問題の規模を示す数値(30個、1000個以上、95%)がPR情報と一致しており、正確です。

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

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

記事のタイトル「filter_parametersの組み合わせ爆発を回避」は、PRのタイトル「Avoid combinatory explosion of `Rails.app.config.filter_parameters`」と完全に一致しており、内容を的確に表しています。

外部知識の正確性 ✓ PASS

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

記事内で言及されている「Rails 8.1」はPRのDescriptionに記載があり、PR情報に基づかない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

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

パフォーマンス問題の発生が過去の事象として記述されており、PR内の時間表現と一致しています。時間表現の歪曲はありません。