コレクションキャッシュでキャッシュストアのデフォルト有効期限を保持するよう修正
この修正により、ActionView::PartialRendererのコレクションキャッシュが、expires_in未指定時にキャッシュストアのデフォルト有効期限を上書きしてしまうリグレッションが解消されました。cached: trueのような簡潔な指定でも、キャッシュストアの設定が意図通りに機能するようになります。
背景
#51579でコレクションキャッシュにexpires_inオプションのサポートが追加された際、意図しないリグレッションが発生していました。cached: trueのようにexpires_inを明示しない場合でも、write_multiメソッドにexpires_in: nilが渡されるようになり、キャッシュストアのデフォルト有効期限が無効化されていました。
#56890で報告されたこの問題により、ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.hour)のようにキャッシュストアのデフォルト有効期限を設定していても、コレクションキャッシュではその設定が無視されていました。一方で、cached: { expires_in: nil }のように明示的に無期限を指定するケースも想定されるため、両者を区別する必要がありました。
技術的な変更
actionview/lib/action_view/renderer/partial_renderer/collection_caching.rbのfetch_or_cache_partialメソッドが修正され、expires_inが明示的に指定された場合のみwrite_multiに渡されるようになりました。
変更前:
unless entries_to_write.empty?
expires_in = @options[:cached][:expires_in] if @options[:cached].is_a?(Hash)
collection_cache.write_multi(entries_to_write, expires_in: expires_in)
end
変更後:
unless entries_to_write.empty?
if @options[:cached].is_a?(Hash) && @options[:cached].key?(:expires_in)
collection_cache.write_multi(entries_to_write, expires_in: @options[:cached][:expires_in])
else
collection_cache.write_multi(entries_to_write)
end
end
@options[:cached].key?(:expires_in)による判定が追加され、expires_inキーの存在を明示的にチェックするようになりました。キーが存在しない場合はwrite_multiにexpires_in引数を渡さないため、キャッシュストアのデフォルト設定が適用されます。キーが存在する場合は、その値(nilを含む)がそのまま渡されます。
テストケースも2つ追加されています。1つ目はcached: { expires_in: nil }と明示した場合に無期限キャッシュとなることを検証し、2つ目はcached: trueの場合にキャッシュストアのデフォルト有効期限(1時間)が適用されることを検証しています。
設計判断
キーの存在チェックによるexpires_inの扱いの分岐が採用されました。
単純な値のチェック(if expires_in)ではなく、key?メソッドによるキーの存在確認を使用することで、expires_in: nilという明示的な指定と、expires_inの省略を区別できます。これにより、「デフォルト設定を使いたい」という暗黙の意図と、「明示的に無期限にしたい」という明示的な意図の両方をサポートできます。
メソッド呼び出しの引数リストを分岐させる実装により、Rubyのキーワード引数の仕組みを活用しています。expires_inキーワードが存在しない場合、write_multiは自身のデフォルト動作(キャッシュストアの設定を使用)に従います。
まとめ
本PRは、コレクションキャッシュのオプション処理において、明示的な指定の有無を正確に判定するよう改善した変更です。キーの存在チェックという小さな修正により、キャッシュストアのデフォルト設定の活用と、明示的な有効期限の指定という2つのユースケースを両立させています。