スキーマキャッシュのカラム・インデックスソートを差し戻し
スキーマキャッシュダンプ時にカラムとインデックスをソートする変更(#54960)が差し戻されました。ソートによって pluck("*") などのクエリの結果順序が変わり、実際のバグにつながることが判明したためです。
背景
元の #54960 は、スキーマキャッシュファイルのカラム・インデックス順序が不安定でGit差分が生じやすい問題(#42717)への対処として導入されました。テーブル名のソートを行った #48824 に続き、各テーブルのカラムとインデックスも名前順にソートして出力するよう encode_with メソッドを拡張したものです。
しかし、このソートはスキーマキャッシュのダンプ時だけでなく、実行時のカラム順序にも影響を与えます。pluck("*") のようにカラムの順序に依存するクエリは、スキーマキャッシュが読み込まれているかどうかによって結果の順序が変わる可能性があり、このPRの作者が実際にその状況に遭遇しています。
schema.rb のソートには順序を保持するためのエスケープハッチが存在しますが、スキーマキャッシュにはそのような回避手段がなく、差し戻しが選択されました。
技術的な変更
encode_with メソッドから、カラムとインデックスを名前でソートする transform_values の呼び出しが削除されました。
変更前(#54960 適用後):
coder["columns"] = @columns.sort.to_h.transform_values { _1.sort_by(&:name) }
coder["indexes"] = @indexes.sort.to_h.transform_values { _1.sort_by(&:name) }
変更後(差し戻し後):
coder["columns"] = @columns.sort.to_h
coder["indexes"] = @indexes.sort.to_h
テーブル名によるソート(.sort.to_h)は #48824 で導入されたものであり、今回の差し戻しでも維持されています。カラムとインデックスのソートのみが取り除かれました。テストコードでも named_values(名前付き構造体の配列)を使ったソート検証が削除され、シンプルなキーバリューペアによる検証に戻っています。
設計判断
Gitの差分削減という利便性よりも、クエリの正確性が優先されました。 スキーマキャッシュはGit管理下に置かれることもありますが、その主たる役割は起動時のデータベースアクセスを省くことであり、カラムの順序情報を正確に保持することが本質的な責務です。
schema.rb とスキーマキャッシュは似た目的を持ちますが、その利用文脈は異なります。schema.rb はDDLの再現を目的とした静的なファイルであり、カラム順序がクエリ結果に直接影響することはありません。一方、スキーマキャッシュはActiveRecordがクエリを構築・解釈する際に参照される動的なメタデータであるため、順序変更の副作用が顕在化しやすい構造です。この非対称性が、同じソート処理であっても schema.rb では許容され、スキーマキャッシュでは問題となる理由です。
まとめ
この差し戻しは、スキーマキャッシュのカラム順序がActiveRecordの実行時動作に直結するという事実を改めて示しています。Git差分の安定化はスキーマキャッシュの主目的ではなく、その改善を行う場合は実行時の順序依存性を切り離す仕組みが必要です。