`track_variants: false` 時のミラーリングで発生する `IntegrityError` を修正

rails/rails

track_variants: false の環境でバリアントをミラーリングすると ActiveStorage::IntegrityError が発生するバグが修正されました。primary.openverify: checksum.present? を渡すことで、チェックサムが存在しない場合の整合性チェックをスキップします。

背景

track_variants: false を設定すると、バリアントのメタデータがデータベースに保存されなくなり、チェックサムも記録されません。その結果、MirrorJobmirror(key, checksum: nil) を呼び出すことになります。

問題は primary.open の内部にある verify_integrity_of メソッドにありました。このメソッドはファイルの実際のMD5ハッシュと引数の checksum を比較しますが、checksumnil の場合は常に不一致となり ActiveStorage::IntegrityError が送出されます。#57164 で報告されたこのバグは、Mirror サービスを使用しつつ track_variants: false を設定しているすべての環境で再現します。

Active Storage のドキュメントには「整合性チェックはチェックサムが存在する場合にのみ行われる」と記載されており、nil チェックサムで整合性チェックが実行されること自体が仕様外の動作でした。

技術的な変更

mirror_service.rbmirror メソッドに対して、primary.open の呼び出しに verify: オプションを追加するという1行の変更が加えられました。

変更前:

primary.open(key, checksum: checksum) do |io|

変更後:

primary.open(key, checksum: checksum, verify: checksum.present?) do |io|

checksum.present?checksumnil または空文字列の場合に false を返すため、整合性チェックが条件付きで実行されるようになります。チェックサムが存在する通常のケースでは verify: true が渡され、従来と同じ動作が維持されます。

テストとして activestorage/test/service/mirror_service_test.rb に「チェックサムなしでのミラーリングが例外を発生させないこと」を確認するケースが追加されています。テストでは checksum: nil を渡して mirror を呼び出し、assert_nothing_raised で正常終了を確認した上で、すべてのミラーにデータが正しく複製されていることも検証しています。

設計判断

verify: オプションによる条件付きスキップ という方式が採用されました。

nil チェックサムを許容するよう verify_integrity_of 自体を変更する方法もありますが、今回は呼び出し側で verify: checksum.present? を明示的に渡すアプローチが取られています。これにより、整合性チェックを省略する意図がコードの呼び出し箇所に明示され、primary.open の汎用的な挙動を変えることなく mirror メソッド固有の要件に対応できます。

また、checksum: nil をそのまま primary.open に渡し続けている点も注目に値します。チェックサムを nil からデフォルト値に置き換えるのではなく、verify: フラグで動作を制御することで、呼び出しシグネチャの一貫性を保っています。

まとめ

本PRは1行の変更でドキュメント通りの動作を実現した修正です。チェックサムが存在しない場合に整合性チェックを呼び出し側で明示的にスキップする設計は、「チェックサムが存在する場合にのみ検証を行う」という既存の仕様をコードレベルで忠実に表現しており、track_variants: false とミラーサービスを組み合わせた構成を安全に利用できるようになります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
827bf268

この記事は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:path/to/file.rb)とGitHubのIssue/PRへのリンク記法([#123](URL))が、ガイドラインに沿って正しく使用されています。

対象読者への適合性 ✓ PASS

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

Active Storageの内部実装に関する内容であり、専門知識を持つエンジニアという対象読者に完全に適合しています。冗長な説明はなく、専門用語が適切に使われています。

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

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

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まっています。1段落1トピックの原則と適切な段落長が守られており、非常に読みやすい構造です。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロック(変更前・変更後)は、提供されたDiff情報と完全に一致しています。テストファイルへの言及も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`track_variants`, `MirrorJob`, `ActiveStorage::IntegrityError`, `checksum` といった技術用語が、PR情報と一致しており、文脈上も正確に使用されています。

説明の技術的正確性 ✓ PASS

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

バグの根本原因(`checksum: nil` での `verify_integrity_of` の失敗)と、その解決策(`verify: checksum.present?` の追加)に関する説明は技術的に正確で、論理的です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescription、Diff、または関連Issueで裏付けられています。「設計判断」セクションはPRに明記されていないものの、コードの変更から導かれる妥当な技術的洞察であり、ハルシネーションには該当しません。

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

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

PR番号(#57170)および関連Issue番号(#57164)が正確に記載されています。

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

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

記事のタイトルはPRの主題(nilチェックサムによるIntegrityErrorの修正)を正確に反映しており、さらに具体的な発生条件(`track_variants: false`)を追記することで、より分かりやすくなっています。

外部知識の正確性 ✓ PASS

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

記事には、PR情報に記載のないバージョンサポート状況やリリース日程などの外部知識は一切含まれていません。

時間表現の正確性 ✓ PASS

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

「修正されました」といった過去形の表現が使われており、PRがマージ済みであるという時間的状況を正確に反映しています。