rupicon 2014 caching
DESCRIPTION
Rupicon 2014 cachingTRANSCRIPT
![Page 1: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/1.jpg)
1 / 58
![Page 2: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/2.jpg)
Rails CachingAlexandru Keszeg
(Software Developer @ PITECH+PLUS)
2 / 58
![Page 3: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/3.jpg)
This is Rails, how hard can it be?
3 / 58
![Page 4: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/4.jpg)
This is Rails, how hard can it be?<% cache @user do %> <%= render partial: 'user' %><% end %>
4 / 58
![Page 5: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/5.jpg)
Thank you for your time
5 / 58
![Page 6: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/6.jpg)
TOPICS
6 / 58
![Page 7: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/7.jpg)
TOPICS
WHY?
fragment caching
SQL caching
HTTP Caching
expiration
7 / 58
![Page 8: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/8.jpg)
WHY?
8 / 58
![Page 9: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/9.jpg)
WHY?
9 / 58
![Page 10: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/10.jpg)
Fragment Cachingcache small pieces of information instead of full pages
10 / 58
![Page 11: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/11.jpg)
1. Do not expire keys
2. Model associations govern expiry
3. Nested caching is best
11 / 58
![Page 12: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/12.jpg)
Do not expire keys
12 / 58
![Page 13: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/13.jpg)
cache_key
user = User.first
=> User id: 1, updated_at: "2014-10-02 18:12:57"
user.cache_key
=> users/1-20141002181257446781000
13 / 58
![Page 14: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/14.jpg)
cache_key
user = User.first
=> User id: 1 , updated_at: "2014-10-02 18:12:57"
user.cache_key
=> users/1-20141002181257446781000
14 / 58
![Page 15: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/15.jpg)
<% cache @user do %> <%= render partial: 'user' %><% end %>
15 / 58
![Page 16: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/16.jpg)
<% cache @user do %> <%= render partial: 'user' %><% end %>
First time we load the pageRead fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)Write fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)
All other page loads:Read fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)
16 / 58
![Page 17: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/17.jpg)
Model associations govern expiry
17 / 58
![Page 18: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/18.jpg)
class Project has_many :todo_lists
class TodoList belongs_to :project, touch: true has_many :todos
class Todo belongs_to :todo_list, touch: true
when something changes, its direct parent is also changed
18 / 58
![Page 19: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/19.jpg)
Nested caching is best
19 / 58
![Page 20: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/20.jpg)
- @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_list| - cache ['todo', todo] do ...todo
when something changes, we don't have to regenerate the entirepage
20 / 58
![Page 21: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/21.jpg)
Before Rails4
- @projects.each do |project| - cache ['v1', 'project', project] do
21 / 58
![Page 22: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/22.jpg)
Rails 4 solves this problem with cache digests.
A call to #cache in your views will now suffix a digest of the template and itsdependencies. No longer will you need to worry about fragment cachedependencies and versioning!
user.cache_key=> users/1-20141002181257446781000
Read fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)
22 / 58
![Page 23: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/23.jpg)
Rails 4 solves this problem with cache digests.
A call to #cache in your views will now suffix a digest of the template and itsdependencies. No longer will you need to worry about fragment cachedependencies and versioning!
user.cache_key=> users/1-20141002181257446781000
Read fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)
23 / 58
![Page 24: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/24.jpg)
Rails 4 solves this problem with cache digests.
A call to #cache in your views will now suffix a digest of the template and itsdependencies. No longer will you need to worry about fragment cachedependencies and versioning!
user.cache_key=> users/1-20141002181257446781000
Read fragment views/users/1-20141002181257446781000/ 95a652b13f0fc5b7bc8785ac31b6b8b9 (1.2ms)
There's a gem for that
rails/cache_digests
24 / 58
![Page 25: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/25.jpg)
Exercise
25 / 58
![Page 26: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/26.jpg)
26 / 58
![Page 27: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/27.jpg)
27 / 58
![Page 28: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/28.jpg)
Perspective data
Or how to make your cache useless
28 / 58
![Page 29: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/29.jpg)
Consider the impact of a single button
29 / 58
![Page 30: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/30.jpg)
.col-sm-12.no-lr-padding.ticket-info .col-price-block %span.top-line From %span.ticket-price.bottom-line = formated_cheapest_ticket_price(activity) .col-tickets-left-block - unless activity.hide_remaining_tickets? %span.color-default.top-line Tickets left: %span.bottom-line = activity_tickets_left(activity) .col-attendees-block %span.top-line Attendees: %span.bottom-line = activity_attendees_count(activity)
.user-buttons-block %a.btn.btn-wishlist.btn-85 %span.event_wishlist_span = wishlisted_label(activity, current_user)
30 / 58
![Page 31: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/31.jpg)
hit rate?cache ['event', event] do
turns into:
cache ['event', event, current_user] do
Hit rate: 1 / Event.count * User.count
*probably should avoid
31 / 58
![Page 32: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/32.jpg)
Solutions
32 / 58
![Page 33: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/33.jpg)
Solutions
Move it client side
.event-pod{data- wishlisted="#{wishlisted_label(activity, current_user)}"}
$('.event-pod').each(function(){ $(this).find('.wishlisted_button').html( $(this.data('wishlisted')) );})
???
This is good if you want to decouple the usere data or generate it in
Also good for edge caching.*extra complexity
33 / 58
![Page 34: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/34.jpg)
There's gem for that!
34 / 58
![Page 35: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/35.jpg)
neighborland/cache_rocket
= render_cached 'outer', replace: 'inner'
outer.html.haml
- cache 'outer' do .lots .of .htmls = cache_replace_key 'inner'
inner.html.haml
= uncacheable_content
35 / 58
![Page 36: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/36.jpg)
36 / 58
![Page 37: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/37.jpg)
benefits
cache more stuff
more hits
faster pages
less stuff in cache
more hits
less RAM used
37 / 58
![Page 38: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/38.jpg)
The fastest code is no code
38 / 58
![Page 39: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/39.jpg)
The fastest code is no code
Leverage the browser cache
39 / 58
![Page 40: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/40.jpg)
def show @article = Article.find(params[:id])
if stale?(:etag => @article, :last_modified => @article.created_at.utc) @statistics = @article.really_expensive_call respond_to do |format| # all the supported formats end endend
40 / 58
![Page 41: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/41.jpg)
def show @article = Article.find(params[:id])
if stale?(:etag => @article, :last_modified => @article.created_at.utc) @statistics = @article.really_expensive_call respond_to do |format| # all the supported formats end endend
41 / 58
![Page 42: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/42.jpg)
def show @article = Article.find(params[:id])
if stale?(:etag => @article, :last_modified => @article.created_at.utc) @statistics = @article.really_expensive_call respond_to do |format| # all the supported formats end endend
=> "304 Not Modified"
42 / 58
![Page 43: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/43.jpg)
Edge cachingCloudfront
Akamai
fastly
Varnish...
43 / 58
![Page 44: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/44.jpg)
Follow these rules
everything common is served in the initial request
perspective data done with js in a different http request
expiration is handled by HTTP headers
44 / 58
![Page 45: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/45.jpg)
Cache-Control: public - Any cache can store a copy of the content.
Cache-Control: private - Don't store, this is for a single user.
Cache-Control: no-cache - Re-validate before serving this content.
Cache-Control: no-store - Don't store this content. Ever. At all. Please.
Cache-Control: public, max-age=[seconds] - Caches can store this content for nseconds.
Cache-Control: s-maxage=[seconds] - Same as max-age but applies specificallyto proxy
45 / 58
![Page 46: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/46.jpg)
What about my models?
46 / 58
![Page 47: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/47.jpg)
class SuggestionsController def show @users = Rails.cache.fetch(['users', params['page']]) do # we need to call .all to hit the database otherwise # we cache only the ruby query object User.suggestions.page(params['page']).all end endend
*all is for rails3
47 / 58
![Page 48: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/48.jpg)
How do i expire that?
48 / 58
![Page 49: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/49.jpg)
How do i expire that?
49 / 58
![Page 51: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/51.jpg)
Tag based cachingInstead of using updated_at to cause a cache miss, record where your objectsare cached
cache @something, tag: ['dashboard', 'settings']
@users = Rails.cache.fetch(['users', params['page']], tag: 'users') do # we need to call .all to hit the database otherwise # we cache only the ruby query object User.suggestions.page(params['page']).allend
...
User#after_commit...
Cashier.expire "users"
51 / 58
![Page 52: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/52.jpg)
But i want hourly updates on my homepage,oh please will you help me?
52 / 58
![Page 53: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/53.jpg)
But i want hourly updates on my homepage,oh please will you help me?
cache 'homepage', expires_in: 1.hour do
53 / 58
![Page 54: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/54.jpg)
This thing doesn't update, i want a buttonto nuke everything!
class NukesController
def create expire_fragment('homepage'); end
end
54 / 58
![Page 55: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/55.jpg)
How well is it working for us?
55 / 58
![Page 56: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/56.jpg)
Where to go from here?
Caching with Rails: An overview
IdentityCache
Multi-fetch Fragments
SecondLevelCache
56 / 58
![Page 57: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/57.jpg)
Thank you
57 / 58
![Page 58: Rupicon 2014 caching](https://reader033.vdocument.in/reader033/viewer/2022060120/5591f5c61a28ab6d0b8b4676/html5/thumbnails/58.jpg)
58 / 58