ねこものがたり

いちにちいっぽ

初めてRedisを使ったキャッシュをやってみました

全然使ったことがなかったのですが、ABテストやりたいなーというのがきっかけでredisを手元で触ってみました。

Rails のキャッシュ機構 - Railsガイド

データ作成

# Product => Product(id: integer, name: string, published_at: datetime, created_at: datetime, updated_at: datetime)
>  1.upto(10).each do |i|
>   Product.create(id: i, name: "product_#{i}", published_at: Time.zone.now)
> end

コード

class ProductsController < ApplicationController
  def show
    @product = Product.find(params[:id])
    fresh_when last_modified: @product.published_at, etag: @product
  end
end


# products/show.html.erb

<h1> product info</h1>
  <p><%= @product.id %></p>
  <p><%= @product.name %></p>

ログ

初回

app/controllers/products_controller.rb:4:in `show'
Started GET "/products/1" for ::1 at 2020-03-01 14:19:10 +0900
   (0.3ms)  SELECT sqlite_version(*)
Processing by ProductsController#show as HTML
  Parameters: {"id"=>"1"}
  Product Load (0.5ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/products_controller.rb:3:in `show'
  Rendering products/show.html.erb within layouts/application
  Rendered products/show.html.erb within layouts/application (Duration: 1.4ms | Allocations: 121)
[Webpacker] Compiling...
[Webpacker] Compiled all packs in /Users/keiko/workspace/practice/ab-test/public/packs
[Webpacker] Hash: 32e57f147dbdcbbf0c82
Version: webpack 4.41.6
Time: 21457ms
Built at: 2020-03-01 14:19:49
                                     Asset       Size       Chunks                         Chunk Names
    js/application-bbe9c4a129ab949e0636.js    124 KiB  application  [emitted] [immutable]  application
js/application-bbe9c4a129ab949e0636.js.map    139 KiB  application  [emitted] [dev]        application
                             manifest.json  364 bytes               [emitted]              
Entrypoint application = js/application-bbe9c4a129ab949e0636.js js/application-bbe9c4a129ab949e0636.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 749 bytes {application} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
    + 3 hidden modules

Completed 200 OK inViews: 39167.4ms | ActiveRecord: 3.2ms | Allocations: 24240)

webpackerのコンパイルが走ってるので結構時間がかかっています。

リロード

Started GET "/products/1" for ::1 at 2020-03-01 14:24:07 +0900
Processing by ProductsController#show as HTML
  Parameters: {"id"=>"1"}
  Product Load (7.2ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/products_controller.rb:3:in `show'
Completed 304 Not Modified in 605ms (ActiveRecord: 7.2ms | Allocations: 1073)

Completed 304 Not Modifiedと出ています。

データを更新して再表示

> Product.find(1).update(name: 'upadted_name')
   (1.5ms)  SELECT sqlite_version(*)
  Product Load (0.3ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
   (0.2ms)  begin transaction
  Product Update (1.2ms)  UPDATE "products" SET "name" = ?, "updated_at" = ? WHERE "products"."id" = ?  [["name", "upadted_name"], ["updated_at", "2020-03-01 05:25:32.072118"], ["id", 1]]
   (3.0ms)  commit transaction
=> true


Started GET "/products/1" for ::1 at 2020-03-01 14:26:05 +0900
Processing by ProductsController#show as HTML
  Parameters: {"id"=>"1"}
  Product Load (0.5ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/products_controller.rb:3:in `show'
  Rendering products/show.html.erb within layouts/application
  Rendered products/show.html.erb within layouts/application (Duration: 1.0ms | Allocations: 9)
[Webpacker] Everything's up-to-date. Nothing to do
Completed 200 OK in 1035ms (Views: 1024.1ms | ActiveRecord: 0.5ms | Allocations: 4675)

Completed 200 OKとなっています。

fresh_whenメソッド

ActionController::ConditionalGet

railsActionControllerにあるメソッドです。

etag

rails guideによると”一致”の捉え方が変わるようです。

弱いETagでは、レスポンスのbodyが微妙に異なっている場合にも同じETagを与えることで、事実上同じレスポンスとして扱えるようになります。
(中略)
強いETagは、弱いETagと異なり、レスポンスがバイトレベルで完全一致することが求められます。巨大な動画やPDFファイル内でRangeリクエストを実行する場合に便利です。

キャッシュをproductionで使う際には「レスポンスが早くても情報が古くては事故になりかねないので、キャッシュを使いこなせないなら使わないほうがいい」ということをアドバイス頂いたことがあるのですが、etagの指定はこのアドバイスと関連しそうです。

感想

ガイドを見ながら使ってみましたが、この記事を書いていて「こうらしい」「ということだそうです」というふうに、自分で断言できない事項がとても多いこともあり、どうやらredisそのものの理解が必要そうです。

あと、私がやりたいのはキャッシュではなくてABテストなので、その角度からも近々やってみます。

全然わかってないのでドットインストールあたりで基本をおさえて行こうと思います。

追記) ドットインストールのredis入門やったのですが、短時間で世界観がざっとおさえられてよかったです。さらっとした部分だけだったので、詳しいredisの操作は公式ドキュメントみながら回数こなしてなれていけたらいいなあ。