Rails HTML Fragment Caching with Cache Rocket

41
and love key-based HTML fragment caching How I learned to stop worrying

description

Key-based fragment caching in Rails is a great way to improve server-side response times. The cache_rocket gem provides a few simple techniques to make html rendering even faster and more efficient. Discussed: usage, performance, and trade-offs of key-based caching techniques.

Transcript of Rails HTML Fragment Caching with Cache Rocket

Page 1: Rails HTML Fragment Caching with Cache Rocket

and����������� ������������������  lovekey-based����������� ������������������  HTML����������� ������������������  fragment����������� ������������������  caching

How����������� ������������������  I����������� ������������������  learnedto����������� ������������������  stop����������� ������������������  worrying

Page 2: Rails HTML Fragment Caching with Cache Rocket

TEE PARHAM@teeparham

Page 3: Rails HTML Fragment Caching with Cache Rocket
Page 4: Rails HTML Fragment Caching with Cache Rocket
Page 5: Rails HTML Fragment Caching with Cache Rocket

Do����������� ������������������  not����������� ������������������  expire����������� ������������������  keys

1

Page 6: Rails HTML Fragment Caching with Cache Rocket

Model����������� ������������������  associationsgovernexpiry

2

Page 7: Rails HTML Fragment Caching with Cache Rocket

3 Nestedcachingis����������� ������������������  best

Page 8: Rails HTML Fragment Caching with Cache Rocket

class Project has_many :todo_lists

class TodoList belongs_to :project, touch: true has_many :todos

class Todo belongs_to :todo_list, touch: true

Page 9: Rails HTML Fragment Caching with Cache Rocket

> todo = Todo.first => #<Todo id: 1, updated_at: "2013-11-19 00:24:01"> > todo.cache_key => "todos/1-20131119002401851149000"

cache_key

Page 10: Rails HTML Fragment Caching with Cache Rocket

> todo = Todo.first => #<Todo id: 1, updated_at: "2013-11-19 00:24:01"> > todo.cache_key => "todos/1-20131119002401851149000"

cache_key

Page 11: Rails HTML Fragment Caching with Cache Rocket

Project

Todo

TodoList

Page 12: Rails HTML Fragment Caching with Cache Rocket

> todo.cache_key=> "todos/1-20131119002401851149000"

> cache(‘todo_item’, todo)=> "todo_item/todos/1-20131119002401851149000"

view����������� ������������������  keys

Page 13: Rails HTML Fragment Caching with Cache Rocket

- @projects.each do |project| - cache [‘project’, project] do ...project... - project.todo_lists.each do |todo_list| - cache [‘todo_list’, todo_list] do ...todo_list... - todo_list.todos.each do |todo| - cache [‘todo’, todo] do ...todo...

Page 14: Rails HTML Fragment Caching with Cache Rocket

YO����������� ������������������  DAWG

Page 15: Rails HTML Fragment Caching with Cache Rocket

has_many����������� ������������������  ����������� ������������������  :arrows

Page 16: Rails HTML Fragment Caching with Cache Rocket

adifferenthammer

Page 17: Rails HTML Fragment Caching with Cache Rocket

User*

City

User

Idea**

Page 18: Rails HTML Fragment Caching with Cache Rocket

YO����������� ������������������  DAWG?*

Page 19: Rails HTML Fragment Caching with Cache Rocket

.stream-item .item-rail .avatar-stream = avatar(idea.user) .mobile-meta.show-for-small Suggested by #{user_link(idea.user)} %br/ = l idea.created_at, format: :short_year .item-pointer .item-content .bubble-stream{class: activity_class(idea)} .item-main{class: ('has-media' if idea.has_stream_media?)} - if idea.has_stream_media? .stream-media = link_to stream_image(idea), idea_path(idea) - if idea.stream_media_video? = link_to "&#9654;".html_safe, idea_path(idea) .text .text-body = want_link(idea) .sub %span.actions = solution_count(idea) = me_too_link(idea)

Page 20: Rails HTML Fragment Caching with Cache Rocket

- cache [‘stream’, idea, current_user] do .all-the-codez = me_too_link(idea)

hit����������� ������������������  rate?

Page 21: Rails HTML Fragment Caching with Cache Rocket

key:����������� ������������������  [idea,����������� ������������������  user]

hit����������� ������������������  rate:����������� ������������������  1����������� ������������������  /����������� ������������������  Idea.count*User.count

hit����������� ������������������  rate

Page 22: Rails HTML Fragment Caching with Cache Rocket

why����������� ������������������  hit����������� ������������������  rate����������� ������������������  matters

Page 23: Rails HTML Fragment Caching with Cache Rocket

= render_cached 'stream/idea', idea: idea, replace: {me_too_link: me_too_link(idea)}

- cache [‘stream’, idea] do .all-the-codez = cache_replace_key :me_too_link

cache_rocket

stream/_idea.html.haml

Page 24: Rails HTML Fragment Caching with Cache Rocket

:me_too_link

cache [‘stream’, idea]

Page 25: Rails HTML Fragment Caching with Cache Rocket

<div class="stream-item"> <div class="item-rail"> <div class="avatar-stream"> <a href="/neighbors/abarad" class="avatar-lnk" data-username="abarad"><img alt="Amy Barad" height="65" src="https://d30wms7jgjmff8.cloudfront.net/avatars/190/streama5e1bed52918ac859ed988618e7a2bad.jpg" width="65"></a> </div> <div class="mobile-meta show-for-small"> Suggested by <a href="/neighbors/abarad">Amy Barad</a><br> Jun 11, 2011 </div> </div> <div class="item-pointer"></div> <div class="item-content"> <div class="bubble-stream main-item"> <div class="has-media item-main"> <div class="stream-media"> <a href="/ideas/nola-the-ability-to-recycle-gl"><img alt="image" height="159" src="https://d30wms7jgjmff8.cloudfront.net/images/107/streamf97520b5053a463e8465195222e98a07.jpg" width="212"></a> </div> <div class="text"> <div class="text-body"> <a href="/ideas/nola-the-ability-to-recycle-gl">398 neighbors want the ability to recycle glass in New Orleans.</a> </div> <div class="sub"> <span class="actions"><a href="/ideas/nola-the-ability-to-recycle-gl#actions" class="btn white-border large"><strong>2</strong> actions</a></span><form action="/votes/803" class="button_to" method="post"> <div> <span class="actions"> <input name="_method" type="hidden" value="delete"> <input class="btn mint large" type="submit" value="Undo"> <input name="authenticity_token" type="hidden" value="vi66rEYBUkiqZVqss7DT/JhDbaN4GSdadEtk34ijRBg="> </span> </div></form> </div> </div> </div> <div class="rule-mint-xlt"></div> <div class="bubble-head new-supporters"> <span>New Supporters</span> <a href="/neighbors/brittwright" class="tiny-avatar-lnk"><img alt="Britt Wright" height="22" src="https://d30wms7jgjmff8.cloudfront.net/assets/avatar_medium_8.png" width="22"></a> <a href="/neighbors/rosemary" class="tiny-avatar-lnk"><img alt="Rosemary" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25636/thumb0d559f628e6e05c8f32a179ba44e80a6." width="22"></a> <a href="/neighbors/willmarshall" class="tiny-avatar-lnk"><img alt="Will M." height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/26049/thumb2ae0e8a2760bcec545ae2655c497e2b1." width="22"></a> <a href="/neighbors/alex5" class="tiny-avatar-lnk"><img alt="Alex" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/26707/thumb511139a75432ee1b6dc9ad6f90b06ca2.jpg" width="22"></a> <a href="/neighbors/mike3" class="tiny-avatar-lnk"><img alt="Mike " height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/24145/thumbc9a0b03a4f25bcbb9f37de0486e4df10.png" width="22"></a> <a href="/neighbors/jaba4017" class="tiny-avatar-lnk"><img alt="Bill Arceneaux" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/3891/thumb6d95461aad25696fc0b4d69a4685f2f8.jpg" width="22"></a> <a href="/neighbors/faubourgstjohn" class="tiny-avatar-lnk"><img alt="Charlie London" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/203/thumb9872b2f76cdbcd64893f8855f73129cb.jpg" width="22"></a> <a href="/neighbors/andrewjameson" class="tiny-avatar-lnk"><img alt="Andrew Jameson" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25316/thumb17689e5da8b64a01fc585438679037d4.jpg" width="22"></a> <a href="/neighbors/jenaoh1" class="tiny-avatar-lnk"><img alt="Jena Oh" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25255/thumb1da559a045c471284e83cfded82f6e82." width="22"></a> <a href="/neighbors/rosemeistr" class="tiny-avatar-lnk"><img alt="rosemeistr" height="22" src="https://d30wms7jgjmff8.cloudfront.net/assets/avatar_medium_1.png" width="22"></a> </div> <div class="bubble-unit top-shadow hide-for-small bottom"> <span class="city-flag"><a href="/cities/nola">New Orleans</a></span> Suggested by <a href="/neighbors/abarad">Amy Barad</a> on Jun 11, 2011 </div> <div class="progress-flag"></div> </div> </div>

me_too_link

HTML����������� ������������������  on����������� ������������������  page

Page 26: Rails HTML Fragment Caching with Cache Rocket

<div class="stream-item"> <div class="item-rail"> <div class="avatar-stream"> <a href="/neighbors/abarad" class="avatar-lnk" data-username="abarad"><img alt="Amy Barad" height="65" src="https://d30wms7jgjmff8.cloudfront.net/avatars/190/streama5e1bed52918ac859ed988618e7a2bad.jpg" width="65"></a> </div> <div class="mobile-meta show-for-small"> Suggested by <a href="/neighbors/abarad">Amy Barad</a><br> Jun 11, 2011 </div> </div> <div class="item-pointer"></div> <div class="item-content"> <div class="bubble-stream main-item"> <div class="has-media item-main"> <div class="stream-media"> <a href="/ideas/nola-the-ability-to-recycle-gl"><img alt="image" height="159" src="https://d30wms7jgjmff8.cloudfront.net/images/107/streamf97520b5053a463e8465195222e98a07.jpg" width="212"></a> </div> <div class="text"> <div class="text-body"> <a href="/ideas/nola-the-ability-to-recycle-gl">398 neighbors want the ability to recycle glass in New Orleans.</a> </div> <div class="sub"> <span class="actions"><a href="/ideas/nola-the-ability-to-recycle-gl#actions" class="btn white-border large"><strong>2</strong> actions</a></span>

<cr me_too_link> </div> </div> </div> <div class="rule-mint-xlt"></div> <div class="bubble-head new-supporters"> <span>New Supporters</span> <a href="/neighbors/brittwright" class="tiny-avatar-lnk"><img alt="Britt Wright" height="22" src="https://d30wms7jgjmff8.cloudfront.net/assets/avatar_medium_8.png" width="22"></a> <a href="/neighbors/rosemary" class="tiny-avatar-lnk"><img alt="Rosemary" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25636/thumb0d559f628e6e05c8f32a179ba44e80a6." width="22"></a> <a href="/neighbors/willmarshall" class="tiny-avatar-lnk"><img alt="Will M." height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/26049/thumb2ae0e8a2760bcec545ae2655c497e2b1." width="22"></a> <a href="/neighbors/alex5" class="tiny-avatar-lnk"><img alt="Alex" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/26707/thumb511139a75432ee1b6dc9ad6f90b06ca2.jpg" width="22"></a> <a href="/neighbors/mike3" class="tiny-avatar-lnk"><img alt="Mike " height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/24145/thumbc9a0b03a4f25bcbb9f37de0486e4df10.png" width="22"></a> <a href="/neighbors/jaba4017" class="tiny-avatar-lnk"><img alt="Bill Arceneaux" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/3891/thumb6d95461aad25696fc0b4d69a4685f2f8.jpg" width="22"></a> <a href="/neighbors/faubourgstjohn" class="tiny-avatar-lnk"><img alt="Charlie London" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/203/thumb9872b2f76cdbcd64893f8855f73129cb.jpg" width="22"></a> <a href="/neighbors/andrewjameson" class="tiny-avatar-lnk"><img alt="Andrew Jameson" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25316/thumb17689e5da8b64a01fc585438679037d4.jpg" width="22"></a> <a href="/neighbors/jenaoh1" class="tiny-avatar-lnk"><img alt="Jena Oh" height="22" src="https://d30wms7jgjmff8.cloudfront.net/avatars/25255/thumb1da559a045c471284e83cfded82f6e82." width="22"></a> <a href="/neighbors/rosemeistr" class="tiny-avatar-lnk"><img alt="rosemeistr" height="22" src="https://d30wms7jgjmff8.cloudfront.net/assets/avatar_medium_1.png" width="22"></a> </div> <div class="bubble-unit top-shadow hide-for-small bottom"> <span class="city-flag"><a href="/cities/nola">New Orleans</a></span> Suggested by <a href="/neighbors/abarad">Amy Barad</a> on Jun 11, 2011 </div> <div class="progress-flag"></div> </div> </div></div>

me_too_link

HTML����������� ������������������  in����������� ������������������  memcache

Page 27: Rails HTML Fragment Caching with Cache Rocket

def replace_from_hash(hash) hash.each do |key, value| gsub! cache_replace_key(key), value.to_s endend

cache_rocketreplace_key = ‘<cr me_too_link>’html = ‘<form><div>etc...</div></form>’fragment.gsub! replace_key, html

Page 28: Rails HTML Fragment Caching with Cache Rocket

gsub!

Page 29: Rails HTML Fragment Caching with Cache Rocket

render_cached 'top', replace: 'inner'

a����������� ������������������  partial

= render ‘inner’= cache_replace_key :inner

top.html.haml

Page 30: Rails HTML Fragment Caching with Cache Rocket

render_cached 'top', replace: ['inner', ‘footer’]

partials

#here = cache_replace_key :inner#there = cache_replace_key :footer

top.html.haml

Page 31: Rails HTML Fragment Caching with Cache Rocket

render_cached 'top', replace: {key: a_helper(x)}

value

= cache_replace_key :keytop.html.haml

Page 32: Rails HTML Fragment Caching with Cache Rocket

render_cached 'item',collection: objects,replace: {key: ->(o){a_helper(o)}}

Proc����������� ������������������  for����������� ������������������  each����������� ������������������  item

= cache_replace_key :keyitem.html.haml

Page 33: Rails HTML Fragment Caching with Cache Rocket

def replace_item_hash(item, replace_hash) item_fragment = self.value.dup

replace_hash.each do |key, proc| item_fragment.gsub! cache_replace_key(key), proc.call(item) end

item_fragmentend

Page 34: Rails HTML Fragment Caching with Cache Rocket

gsub!

Page 35: Rails HTML Fragment Caching with Cache Rocket

extra����������� ������������������  string����������� ������������������  replacementdifferent����������� ������������������  render����������� ������������������  syntaxadds����������� ������������������  a����������� ������������������  dependency

cost

Page 36: Rails HTML Fragment Caching with Cache Rocket

cache����������� ������������������  more����������� ������������������  stuff>����������� ������������������  more����������� ������������������  hits>����������� ������������������  faster����������� ������������������  pages

less����������� ������������������  stuff����������� ������������������  in����������� ������������������  cache>����������� ������������������  more����������� ������������������  hits>����������� ������������������  less����������� ������������������  RAM

benefit

Page 37: Rails HTML Fragment Caching with Cache Rocket

No Caching 123 msRussian Doll* 67 msCacheRocket 113 msCacheRocket collection 82 ms

speed

Page 38: Rails HTML Fragment Caching with Cache Rocket

efficiency Hits MissesRussian Doll 1400 100CacheRocket 14 1

Page 39: Rails HTML Fragment Caching with Cache Rocket

efficiency Items SizeRussian Doll 100 120.9kCacheRocket 1 0.9k

Page 40: Rails HTML Fragment Caching with Cache Rocket

Cat