パラメータフィルタの冗長なパターンを最適化

rails/rails

Active Recordの暗号化機能が自動登録するフィルタパターンは、暗号化カラムの増加に伴い肥大化し、パフォーマンスに深刻な影響を与えていました。本PRは、プリコンパイル段階で冗長なパターンを削除し、この問題を解決しています。

背景

Active Recordの暗号化機能は、暗号化された全ての属性に対して自動的にdeepパターン(user.password のようなネストしたパス)を登録します。例えば _token サフィックスを持つ暗号化カラムが多数存在する場合、user.api_tokenuser.access_tokenadmin.session_token といったパターンが個別に登録されますが、既に token という単純なパターンが登録されていればこれらは全て冗長です。

フィルタパターンのリストが急速に肥大化すると、各リクエストでのパラメータフィルタリング処理のコストが増大します。#56762 は、プリコンパイルステップで冗長なパターンを削除することでこの問題に対処しています。

技術的な変更

ActiveSupport::ParameterFilter.precompile_filters メソッドが、パターンの包含関係を検証する処理を追加しました。

変更前:

patterns.map! do |pattern|
  pattern.is_a?(Regexp) ? pattern : "(?i:#{Regexp.escape pattern.to_s})"
end

deep_patterns = patterns.extract! { |pattern| pattern.to_s.include?("\\.") }

filters << Regexp.new(patterns.join("|")) if patterns.any?
filters << Regexp.new(deep_patterns.join("|")) if deep_patterns.any?

変更後:

regexps, patterns = patterns.partition { |filter| filter.is_a?(Regexp) }

patterns.map! do |p|
  p.is_a?(Symbol) ? p.name : p.to_s
end

patterns.sort_by! { |p| [p.count("."), p.size] }

patterns.each do |pattern|
  unless regexps.any? { |r| r.match?(pattern) }
    regexps << Regexp.new(Regexp.escape(pattern), Regexp::IGNORECASE)
  end
end

filters << Regexp.union(regexps)

主要な変更点は以下の3つです:

  • パターンの分離: まず既存のRegexpパターンを regexps 配列に分離し、文字列/シンボルパターンのみを処理対象とします
  • ソート処理: パターンをドット数と長さでソートし、より広範なパターン(password など)が先に処理されるようにします
  • 冗長性チェック: 各パターンを追加する前に、既存の正規表現パターンにマッチするかを regexps.any? { |r| r.match?(pattern) } で検証します。マッチする場合は冗長とみなして追加をスキップします

テストケースでは、この最適化の効果が検証されています:

test "precompile_filters eliminate dead patterns" do
  assert_equal [/token/i], ActiveSupport::ParameterFilter.precompile_filters(["user.token", "token"])
  assert_equal [/password/i], ActiveSupport::ParameterFilter.precompile_filters(["user_password", "password"])
end

["user.token", "token"][/token/i] に、["user_password", "password"][/password/i] に集約されます。より広範なパターンが存在する場合、特定的なパターンは自動的に削除される仕組みです。

設計判断

deepパターンと通常パターンの区別を廃止し、全てを統合した単一の正規表現にまとめる方式 が採用されました。

変更前は patternsdeep_patterns を別々の正規表現として保持していましたが、変更後は Regexp.union で単一の正規表現に統合しています。テストの変更からもこの統合が確認できます:

# 変更前: 2つの正規表現が生成される
assert_equal 2, precompiled.grep(Regexp).length

# 変更後: 1つの正規表現に統合される
assert_equal 1, precompiled.grep(Regexp).length

この設計により、パターンマッチングの処理が一度の正規表現評価で完結します。また、ソート処理(sort_by! { |p| [p.count("."), p.size] })により、ドット数が少ない(=より広範な)パターンを優先的に処理し、後続の特定的なパターンを効率的に排除する仕組みを実現しています。

まとめ

本PRは、Active Recordの暗号化機能による大量のフィルタパターン登録という実用上の課題に対し、プリコンパイル段階での冗長性削除という解決策を提示しています。パターンの包含関係を検証するシンプルなロジックの追加により、パフォーマンス劣化を引き起こす冗長なパターンを自動的に排除し、フィルタリング処理の効率化を実現しました。

記事メタデータ

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

ファイル名付きシンタックスハイライト(```ruby:path/to/file.rb)とPR番号のリンク記法([#56762](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

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

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

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

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

Diff内容との照合 ⚠ WARNING

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

変更後コードとテストコードの引用は正確ですが、「変更前」のコードブロックがDiffで削除された部分を完全には反映しておらず、一部の文脈が省略されています。ただし、変更の要旨を理解する上では大きな問題ではありません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「deepパターン」「プリコンパイル」「Regexp.union」など、関連する技術用語が正確かつ適切な文脈で使用されています。

説明の技術的正確性 ✓ PASS

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

パターンのソート処理や冗長性チェックのロジック、単一正規表現への統合といった技術的な説明は、Diffの内容と整合しており、正確かつ論理的です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescriptionやDiff内のコード変更に基づいており、根拠のない推測や創作(ハルシネーション)は見られません。

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

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

PR番号(#56762)やファイルパス、テストコード内のアサーション(`assert_equal 1, ...`)など、数値や固有名詞はすべて正確です。

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

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

記事のタイトル「パラメータフィルタの冗長なパターンを最適化」は、PRのタイトル「ActiveSupport::ParameterFilter: optimize redundant patterns out」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

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

PR情報に記載のないバージョンサポート状況やリリース日程などの外部知識は含まれておらず、事実に忠実です。

時間表現の正確性 ✓ PASS

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

「〜していました」「〜に対処しています」といった時間表現は、PRの文脈(既存の問題への修正)と一致しており、正確です。