Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Railsでのロシアの人形のキャッシュ

今日は、Railsの組み込みフラグメントキャッシングを超えてキャッシングをさらに改善するための戦術として、ロシアのドールキャッシングについて詳しく説明します。

フラグメントキャッシング

Railsの組み込みフラグメントキャッシングを使用する場合、レンダリングされたビューの一部はビューフラグメントとして保存され、再度要求された場合に再利用されます。これらのキャッシュされたフラグメントは、古くなるまで再利用されます。 、つまり、フラグメントの作成後に表示されるデータが変更されたため、古くなっています。詳細をお読みになりたい場合は、この以前の投稿で、フラグメントキャッシングについてもう少し詳しく説明します。

👋キャッシュだけでなく、パフォーマンスについて詳しく知りたい場合は、Rubyパフォーマンス監視チェックリストを確認してください。

これにより、特に複雑なビューやレンダリングされたパーシャルが多数あるビューの速度が大幅に向上しますが、ロシアンドールキャッシングという名前のアプローチを2倍にすることで、これをさらに改善できます。 。

このキャッシングアプローチを使用する場合、戦略の名前にちなんで名付けられた「マトリョーシカ」人形のように、ビューフラグメントは互いに内部に配置されます。キャッシュされたフラグメントをより小さな部分に分割することにより、ネストされたフラグメントの1つだけが変更されたときに、外部キャッシュをより高速にレンダリングできます。

ロシアの人形のキャッシュの例

例として、商品を販売する店を使用してみましょう。各製品には、たとえば1つのアイテムの複数の色を販売できるようにするさまざまなバリエーションを含めることができます。インデックスには、販売可能な各製品とそのすべてのバリエーションが表示されます。

商品インデックスでは、各商品を cacheで部分的にラップしています。 ブロック。 productを使用しています キャッシュされたフラグメントを無効にするために使用されるキャッシュキーを構築するオブジェクト。これは、オブジェクトのID、updated_at date、およびテンプレートツリーのダイジェストで構成されているため、オブジェクトが変更された場合、またはテンプレートのコンテンツが変更された場合、自動的に古くなったと見なされます。

# app/views/products/index.html.erb
<h1>Products</h1>
 
<% @products.each do |product| %>
  <% cache product do %>
    <%= render product %>
  <% end %>
<% end %>

ヒント :わかりやすくするためにブロック全体を書き出していますが、 <%=render partial:'products / product'、collection:@products、cached:true%> <を使用して、各製品をキャッシュブロックにレンダリングできます。 / code> 代わりに。

製品の部分では、製品のバリエーションごとに1行をレンダリングします。

# app/views/products/_product.html.erb
<article>
  <h1><%= product.title %></h1>
 
  <ul>
    <% product.variants.each do |variant| %>
      <%= render variant %>
    <% end %>
  </ul>
</article>

キャッシュの無効化

Railsのフラグメントキャッシングのキャッシュキーを使用すると、キャッシュの無効化が容易になりますが、キャッシュの検証(コンピューターサイエンスで有名な2つの難しいことの1つ)について心配する必要はありません。

この例では、製品のバリアントのリストを含む製品を部分的にキャッシュします。キャッシュキーにはバリアントに関する情報が含まれていないため、製品自体も変更されない限り、新しく追加されたバリアントは表示されません。

これを修正する方法は、製品が動作することを確認することです。 そのバリアントの1つで何かが変更されたときに変更します。そのために、製品の updated_atを更新します それが起こるときはいつでも属性。これは非常に一般的であるため、 belongs_toの引数があります (およびActiveModelの他のリレーションメソッド)、:touchと呼ばれます 、これにより、親オブジェクトのupdated_atが自動的に更新されます。

class Variant < ApplicationRecord
  belongs_to :product, touch: true
end

ネストされたフラグメント

バリアントが変更されたときに製品フラグメントを更新することを確認したので、バリアントもキャッシュするときが来ました。前と同じように、 cacheを追加します それぞれの周りをブロックします。

<article>
  <h1><%= product.title %></h1>
 
  <ul>
    <% product.variants.each do |variant| %>
      <% cache(variant) do %>
        <%= render variant %>
      <% end %>
    <% end %>
  </ul>
</article>

ヒント :わかりやすくするためにブロック全体を書き出していますが、 <%=render partial:'variants / Variant'、collection:product.variants、cached:true%>を使用して、キャッシュブロック内の各バリアントをレンダリングできます。 代わりに。

コールドキャッシュの場合( rake tmp:cache:clearを実行してキャッシュをクリアできます )、最初のリクエストで各商品が部分的にレンダリングされます。

今すぐページをリクエストする場合( rails dev:cache を実行して、開発時にキャッシュをオンにすることを忘れないでください )、各製品のパーシャルはパーシャルとしてキャッシュされ、2番目のリクエストはキャッシュされたフラグメントを返します。

Started GET "/products" for 127.0.0.1 at 2018-03-30 14:51:38 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.2ms)  SELECT "products".* FROM "products"
  Variant Load (0.9ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered variants/_variant.html.erb (0.5ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.0ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered products/_product.html.erb (44.8ms) [cache miss]
  ...
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered variants/_variant.html.erb (0.1ms)
  Rendered products/_product.html.erb (46.2ms) [cache miss]
  Rendered products/index.html.erb within layouts/application (1378.6ms)
Completed 200 OK in 1414ms (Views: 1410.5ms | ActiveRecord: 1.1ms)


Started GET "/products" for 127.0.0.1 at 2018-03-30 14:51:41 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT "products".* FROM "products"
  Variant Load (12.7ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered products/index.html.erb within layouts/application (48.1ms)
Completed 200 OK in 76ms (Views: 59.0ms | ActiveRecord: 13.0ms)

ボイラ:ロシアの人形の魔法

ロシアの人形のキャッシュの魔法は、バリアントの1つを変更するときに見ることができます。バリアントの1つが変更された後にインデックスを再度要求すると、キャッシュされた製品フラグメントが再レンダリングされます。これは、その updated_atが原因です。 属性が変更されました。

製品の部分には、製品の各バリエーションが含まれます。変更したばかりのバリアントのキャッシュされたフラグメントは古くなっているため、再生成する必要がありますが、他のバリアントは変更されていないため、キャッシュされたフラグメントが再利用されます。ログでは、バリアントと製品の両方のパーシャルが1回レンダリングされていることがわかります。

Started GET "/products" for 127.0.0.1 at 2018-03-30 14:52:04 +0200
Processing by ProductsController#index as HTML
  Rendering products/index.html.erb within layouts/application
  Product Load (0.3ms)  SELECT "products".* FROM "products"
  Variant Load (1.2ms)  SELECT "variants".* FROM "variants" WHERE "variants"."product_id" IN (27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51)
  Rendered variants/_variant.html.erb (0.5ms)
  Rendered products/_product.html.erb (13.3ms) [cache miss]
  Rendered products/index.html.erb within layouts/application (45.9ms)
Completed 200 OK in 78ms (Views: 73.5ms | ActiveRecord: 1.5ms)

最終結果

このようにキャッシュフラグメントをネストすることにより、キャッシュが完全に空でない限り、ビュー全体がレンダリングされることはほとんどありません。データが変更された場合でも、レンダリングされたページのほとんどはキャッシュから直接提供されます。

これが、アプリのパフォーマンスに関する新しい洞察を得るのに役立つことを願っています。キャッシングに関するこの記事が好きで、もっと欲しがっているなら、ActiveRecordのカウンターキャッシュに関する投稿、Railsのキャッシュストアに関する投稿、コレクションキャッシングに関する投稿、フラグメントに関する投稿があります。投稿で前述したRailsでのキャッシュ。


  1. RailsをAWSLambdaにデプロイする

    サーバーレスコンピューティングは、サーバーの管理とプロビジョニングの作業をクラウドプロバイダーに任せるのに役立ち、ほとんどのテクノロジーチームにとって急速に重要になっています。 AWS Lambdaは、多くのテクノロジーチームで使用されているサーバーレステクノロジーの一種です。 AWS Lambdaは、NodeJS、Java、Python、Rubyなどのコアプログラミング言語のほとんどをサポートしています。コアプログラミング言語はサポートされていますが、これらの言語で構築されたフレームワークの一部である機能に依存してサーバーレス関数を実行したい場合があります。この投稿では、AWSLambdaで

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた