🦊

Rails内でETagはどのように利用されているのか

に公開

改めてETagってなんだったっけ・・・?と思ったので軽くまとめました!網羅的ではないけど最低限理解するためのリソースはまとまっていると思います。

そもそもETagとは

参考にしたのはこちら
https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/Conditional_requests
サーバーへのリクエストヘッダーにETagと最後に更新された日時を示すlast-modifiedを送ることでキャッシュが有効かどうかを確認するもの

Last-Modified で強い検証のための一意な識別子を持つことは、とても困難です。多くの場合、リソースの MD5 ハッシュ(あるいはその派生物)を持つ ETag を使用します。

最終更新日だけでは該当ドキュメントを更新したかどうかを確認するのは難しいのでそのハッシュ値であるETagを用いるということぽい
参考にさせていただいたmdnドキュメント内では使用例がまとまっているので詳しい内容はそちらを見てください
強い検証(Strong validation)と弱い検証(Weak validation)という概念がありどこまで厳密にしたいかによって使い分ける。デフォルトは強い検証

早速Rails内でETagはどのように利用されているか見てみる

Fizzy内での利用をみてみる

最近basecampからリリースされたオープンソースのkanbanシステムの https://fizzy.do を見てみる

https://github.com/basecamp/fizzy/blob/fizzy%403fcf5a9/app/controllers/cards/boards_controller.rb#L7-L10

APIドキュメントも見てみる
https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-fresh_when

fresh_whenメソッドはETagとlast-modifiedをセットしてもしリクエストがフレッシュ(つまりETagとlast-modifiedでキャッシュが陳腐化していなければ)だったらその場で304 Not Modifiedを返して終了するメソッド

以下のコードはほぼ同義

fresh_when @post
fresh_when(etag: @post, last_modified: @post.updated_at, public: true) # @post.updated_atを持っている場合
fresh_when @boards
fresh_when(etag: @boards, last_modified: @boards.try(:maximum, :updated_at)) # Relationの場合はその中で一番最新のupdated_atでlast-modifiedとする

なので例として出したFizzyの cards/boards_controller.rb 内で fresh_when @boards とした場合、キャッシュの更新をするためにレスポンスを返す必要があるか確認してもし必要がない場合はここで304を返してrenderはされない
もしフレッシュではなく陳腐化していた場合はrenderされるって流れ

フレッシュかどうかだけ確認したい場合

stale?というメソッドがある
https://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-stale-3F
内部的にはfresh_whenを呼び出した後にrequestがフレッシュかどうかを確認している
stale?だと条件分岐が書けるため、重い集計処理とかを走らせる前に確認すると便利そう

def show
  @post = Post.find(params[:id])

  if stale?(@post) # stale のときだけ実行される
    render :show
  end
  # stale? == false だったら 304が返る
end

Fizzyのアプリ内でも何個かstale?を使って条件分岐している。
ここの例だとUserのavatarが存在しない場合で、デフォルト画像がフレッシュではない場合にsvgを返しているぽい
https://github.com/basecamp/fizzy/blob/3fcf5a9d08a8903ec7f84de86c217c0fbf311602/app/controllers/users/avatars_controller.rb#L7-L15

ちなみにstaleという英単語は(鮮度が)古いという品質の低下を表す単語だそう

終わりに

他にもRack::ETagというものが存在していたり、etagに配列を渡して複数条件を付け加えることが出来たりできるぽいですしmdn内の部分ダウンロードを実装するならどうするのかとか気になることもありますがそれはまたの機会があれば調べてみたいです。

Discussion