Page Caching Resurrected

Ben Sco eld – Viget Labs
Page Caching Resurrected:
A Fairy Tale
Ben Sco eld – Viget Labs
three little pigs
three little pigs
page caching
class PigsController
 caches_page :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
  @pig = Pig.find(params...
class PigsController
 caches_page :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
  @pig = Pig.find(params...
class PigsController
 caches_page :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
  @pig = Pig.find(params...
standard   1




   page    3000




                  requests per second
public
dynamic
content
action caching
class PigsController
 require_login :show
 caches_action :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
...
class PigsController
 require_login :show
 caches_action :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
...
class PigsController
 require_login :show
 caches_action :show

 def show
  @pig = Pig.find(params[:id])
 end

 def update
...
standard   1



  action   500



   page    3000




                  requests per second
dynamic
content
fragment caching
<% cache(:key => 'index:piglist') do %>
 <ul id=quot;pig-listquot;>
  <% @pigs.each do |pig| %>
   <li>
     <%= image_tag...
<% cache(:key => 'index:piglist') do %>
 <ul id=quot;pig-listquot;>
  <% @pigs.each do |pig| %>
   <li>
     <%= image_tag...
class PigsController
 # ...

 def update
  @pig = Pig.find(params[:id])
  @pig.update_attributes(params[:pig])

  expire_fr...
class PigsController
 # ...

 def update
  @pig = Pig.find(params[:id])
  @pig.update_attributes(params[:pig])

  expire_fr...
standard   1


fragment   20


  action   500


   page    3000




                  requests per second
eficiency
the idea
Rails 2.3
hello, metal
class BigBadWolf
 def self.call(env)
   if env[quot;PATH_INFOquot;] =~ /^/threaten/
     [
       200,
       {quot;Conten...
high-performance endpoints
AJAX
so...
javascript
ul id=quot;logged-out-navquot; class=quot;usernavquot; style=quot;display:nonequot;
 li class=quot;first sign-out-linkquot;...
div id=quot;top-login-formquot; class=quot;usernav login-formquot; style=quot;display:nonequot;
 form action=quot;https://...
ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot;
 li id=quot;welcome-usernamequot; class...
noscript
 ul class=quot;nojs usernavquot;
  li
   Please a href=quot;/support/quot;enable javascript/a to log in.
  /li
 /...
script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/
 Event.observe(window, quot;loadquot;, function(...
ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot;
 li id=quot;welcome-usernamequot; class...
ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot;
 li id=quot;welcome-usernamequot; class...
hulu is insane
 don’t do it like they do
script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/
 Event.observe(window, quot;loadquot;, function(...
script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/
 Event.observe(window, quot;loadquot;, function(...
the* right way


      *a
the standard approach
class ItemsController  ApplicationController
 def index
   @user = User.find_by_login(params[:user])
   @items = Item.relea...
class Item  ActiveRecord::Base
 has_many :pulls, :dependent = :destroy

 named_scope :released_on, lambda { |date|
   date...
h1Releases/h1

ul
 % @items.each do |item| %
  % content_tag_for :li, item do %
   %= image_tag 'cover.png', :alt = h(item...
the hybrid approach
class ItemsController  ApplicationController
 caches_page :index

 def index
  @items = Item.released_on(params[:date]).al...
require(File.dirname(__FILE__) + quot;/../../config/environmentquot;) unless
 defined?(Rails)

class PullList
 def self.call...
class Pull  ActiveRecord::Base
 belongs_to :user
 belongs_to :item

 named_scope :by_user, lambda { |user_id|
   {:conditi...
$(document).ready(function() {
  $.getJSON('/pulls', function(data) {
    $.each(data, function() {
      $('#item_'+this)...
standard
hybrid
standard   0.617




  hybrid   0.039 0.096




                         content load time
hybrid approach - version 2
class SessionsController  ApplicationController
 def new; end

 def create
  # ...
  session[:pulls] = @user.pulls.map {|i...
require(File.dirname(__FILE__) + quot;/../../config/environmentquot;) unless
 defined?(Rails)

class PullList
 def self.call...
hybrid v2
standard    0.617



  hybrid    0.039 0.096



hybrid v2   0.043 0.023




                          content load time
when to use it
when not to use it
accessibility
accessibility
noscript
 ul class=quot;nojs usernavquot;
  li
   Please a href=quot;/support/quot;enable javascript/a to log in.
  /li
 /...
Thank You
ben sco eld - @bsco eld - http://www.viget.com/extend - http://www.speakerrate.com/bsco eld
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Page Caching Resurrected
Upcoming SlideShare
Loading in...5
×

Page Caching Resurrected

1,544

Published on

This is the revised version of my Page Caching Resurrected presentation, as given to CVREG in April 2009.

Published in: Lifestyle, Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,544
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
41
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide





  • super simple to use, but results in PUBLIC content
  • super simple to use, but results in PUBLIC content




  • more framework-specific; solves auth-protected pages, etc. - cached content is not publicly available
  • more framework-specific; solves auth-protected pages, etc. - cached content is not publicly available




  • caches only specific parts of the page, which are not publically available
  • expiration of each fragment, individually


  • 10 minutes








  • 15 minutes












  • 22 minutes







  • 26 minutes






  • 135ms for hybrid, which is 21% of the standard

    lame tests I employed showed the same or greater effect
  • 26 minutes



  • 66ms for hybrid v2, 10.6% of standard and 48% of hybrid
  • 34 minutes









  • Page Caching Resurrected

    1. 1. Page Caching Resurrected Ben Sco eld – Viget Labs
    2. 2. Page Caching Resurrected: A Fairy Tale Ben Sco eld – Viget Labs
    3. 3. three little pigs
    4. 4. three little pigs
    5. 5. page caching
    6. 6. class PigsController caches_page :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_page :action => :show end end
    7. 7. class PigsController caches_page :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_page :action => :show end end
    8. 8. class PigsController caches_page :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_page :action => :show end end
    9. 9. standard 1 page 3000 requests per second
    10. 10. public dynamic content
    11. 11. action caching
    12. 12. class PigsController require_login :show caches_action :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_action :action => :show end end
    13. 13. class PigsController require_login :show caches_action :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_action :action => :show end end
    14. 14. class PigsController require_login :show caches_action :show def show @pig = Pig.find(params[:id]) end def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_action :action => :show end end
    15. 15. standard 1 action 500 page 3000 requests per second
    16. 16. dynamic content
    17. 17. fragment caching
    18. 18. <% cache(:key => 'index:piglist') do %> <ul id=quot;pig-listquot;> <% @pigs.each do |pig| %> <li> <%= image_tag pig.house.photo %> <%= h pig.name %> </li> <% end %> </ul> <% end %>
    19. 19. <% cache(:key => 'index:piglist') do %> <ul id=quot;pig-listquot;> <% @pigs.each do |pig| %> <li> <%= image_tag pig.house.photo %> <%= h pig.name %> </li> <% end %> </ul> <% end %>
    20. 20. class PigsController # ... def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_fragment :key => 'index:piglist' end end
    21. 21. class PigsController # ... def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_fragment :key => 'index:piglist' end end
    22. 22. standard 1 fragment 20 action 500 page 3000 requests per second
    23. 23. eficiency
    24. 24. the idea
    25. 25. Rails 2.3
    26. 26. hello, metal
    27. 27. class BigBadWolf def self.call(env) if env[quot;PATH_INFOquot;] =~ /^/threaten/ [ 200, {quot;Content-Typequot; = quot;text/htmlquot;}, quot;Little pig, little pig, let me in!quot; ] else [ 404, {quot;Content-Typequot; = quot;text/htmlquot;}, quot;Not Foundquot; ] end end end
    28. 28. high-performance endpoints
    29. 29. AJAX
    30. 30. so...
    31. 31. javascript
    32. 32. ul id=quot;logged-out-navquot; class=quot;usernavquot; style=quot;display:nonequot; li class=quot;first sign-out-linkquot; a class=quot;utility-linkquot; href=quot;#quot; id=quot;login-linkquot; onclick=quot;Login.show($('top-login-form')); return false;quot;Login/a /li li class=quot;signin-border-leftquot; a href=quot;/users/forgot_passwordquot; class=quot;utility-linkquot;Forgot Password?/a /li li class=quot;signin-border-leftquot; a href=quot;/signupquot; class=quot;utility-linkquot;Sign Up/a /li /ul
    33. 33. div id=quot;top-login-formquot; class=quot;usernav login-formquot; style=quot;display:nonequot; form action=quot;https://secure.hulu.com/account/authenticatequot; method=quot;getquot; name=quot;login-formquot; onsubmit=quot;Login.submit(this); return false;quot; span class=quot;login-statusquot;/span input id=quot;loginquot; class=quot;active loginquot; type=quot;textquot; style=quot;display: none;quot; name=quot;usernamequot;/ input id=quot;passwordquot; class=quot;active passwordquot; type=quot;passwordquot; style=quot;display: none;quot; name=quot;passwordquot;/ input class=quot;inactive dummy loginquot; type=quot;textquot; value=quot;usernamequot; name=quot;dummy_loginquot; / input class=quot;inactive dummyquot; type=quot;textquot; value=quot;passwordquot; name=quot;dummy_passwordquot; / input alt=quot;Loginquot; class=quot;login-submitquot; src=quot;http://static.hulu.com/images/btn-signin-small.gif?1237361096quot; style=quot;width: 39px; height: 20;quot; type=quot;imagequot; / a href=quot;#quot;img alt=quot;Cancelquot; border=quot;0quot; class=quot;hover-mequot; height=quot;20quot; id=quot;btn-x.gif123741536309506quot; onclick=quot;Login.cancel();return falsequot; src=quot;http://static.hulu.com/images/btn-x.gif?1237361096quot; width=quot;18quot; //a /form a href=quot;/signupquot; class=quot;utility-linkquot;Sign Up/a /div
    34. 34. ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot; li id=quot;welcome-usernamequot; class=quot;firstquot; Welcome /li li a href=quot;/profilequot; class=quot;utility-linkquot;Profile/a /li li a href=quot;/profile/queuequot; class=quot;utility-linkquot; id=quot;your-queue-linkquot;Queue/a /li li class=quot;sign-out-linkquot; a href=quot;#quot; id=quot;top-nav-sign-outquot; onclick=quot;new Ajax.Request(...)quot;Sign Out/a /li /ul
    35. 35. noscript ul class=quot;nojs usernavquot; li Please a href=quot;/support/quot;enable javascript/a to log in. /li /ul /noscript
    36. 36. script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/ Event.observe(window, quot;loadquot;, function() { Behaviors.onLoad(); Behaviors.setLoggedIn(); Login.setup(); if ($(quot;loginquot;) $(quot;login-formquot;)) { Event.observe('login', 'keydown', function(ev) { if (ev.keyCode == 13) { Login.submit(); } }); } }); Login.auth = function(login, password) { document.cookie = 'login=' + login + '; path=/account/authenticate; secure; ... document.cookie = 'password=' + password + '; path=/account/authenticate; ... el = $$('body').first(); if (el) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = quot;https://secure.hulu.com/account/authenticatequot; + '?' + ... el.appendChild(script); } } /*]]*//script
    37. 37. ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot; li id=quot;welcome-usernamequot; class=quot;firstquot; Welcome /li li a href=quot;/profilequot; class=quot;utility-linkquot;Profile/a /li li a href=quot;/profile/queuequot; class=quot;utility-linkquot; id=quot;your-queue-linkquot;Queue/a /li li class=quot;sign-out-linkquot; a href=quot;#quot; id=quot;top-nav-sign-outquot; onclick=quot;new Ajax.Request(...)quot;Sign Out/a /li /ul
    38. 38. ul id=quot;logged-in-navquot; class=quot;usernavquot; style=quot;display:nonequot; li id=quot;welcome-usernamequot; class=quot;firstquot; Welcome /li li a href=quot;/profilequot; class=quot;utility-linkquot;Profile/a /li li a href=quot;/profile/queuequot; class=quot;utility-linkquot; id=quot;your-queue-linkquot;Queue/a /li li class=quot;sign-out-linkquot; a href=quot;#quot; id=quot;top-nav-sign-outquot; onclick=quot;new Ajax.Request(...)quot;Sign Out/a /li /ul
    39. 39. hulu is insane don’t do it like they do
    40. 40. script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/ Event.observe(window, quot;loadquot;, function() { Behaviors.onLoad(); Behaviors.setLoggedIn(); Login.setup(); if ($(quot;loginquot;) $(quot;login-formquot;)) { Event.observe('login', 'keydown', function(ev) { if (ev.keyCode == 13) { Login.submit(); } }); } }); Login.auth = function(login, password) { document.cookie = 'login=' + login + '; path=/account/authenticate; secure; ... document.cookie = 'password=' + password + '; path=/account/authenticate; ... el = $$('body').first(); if (el) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = quot;https://secure.hulu.com/account/authenticatequot; + '?' + ... el.appendChild(script); } } /*]]*//script
    41. 41. script type=quot;text/javascriptquot; charset=quot;utf-8quot;/*![CDATA[*/ Event.observe(window, quot;loadquot;, function() { Behaviors.onLoad(); Behaviors.setLoggedIn(); Login.setup(); if ($(quot;loginquot;) $(quot;login-formquot;)) { Event.observe('login', 'keydown', function(ev) { if (ev.keyCode == 13) { Login.submit(); } }); } }); Login.auth = function(login, password) { document.cookie = 'login=' + login + '; path=/account/authenticate; secure; ... document.cookie = 'password=' + password + '; path=/account/authenticate; ... el = $$('body').first(); if (el) { var script = document.createElement('script'); script.type = 'text/javascript'; script.src = quot;https://secure.hulu.com/account/authenticatequot; + '?' + ... el.appendChild(script); } } /*]]*//script
    42. 42. the* right way *a
    43. 43. the standard approach
    44. 44. class ItemsController ApplicationController def index @user = User.find_by_login(params[:user]) @items = Item.released_on(params[:date]) end end
    45. 45. class Item ActiveRecord::Base has_many :pulls, :dependent = :destroy named_scope :released_on, lambda { |date| date ||= Item.maximum(:released_on) {:conditions = {:released_on = date}, :order = 'name ASC'} } def pulled_by?(user) x = user.nil? ? false : !self.pulls.by_user(user.id).empty? end end
    46. 46. h1Releases/h1 ul % @items.each do |item| % % content_tag_for :li, item do % %= image_tag 'cover.png', :alt = h(item.name), :class = 'cover' % %= image_tag('badge.png', :alt = 'pulling',:class = 'badge') if item.pulled_by?(@user) % p%= h item.name %/p % end % % end % /ul
    47. 47. the hybrid approach
    48. 48. class ItemsController ApplicationController caches_page :index def index @items = Item.released_on(params[:date]).all end end
    49. 49. require(File.dirname(__FILE__) + quot;/../../config/environmentquot;) unless defined?(Rails) class PullList def self.call(env) if env[quot;PATH_INFOquot;] =~ /^/pulls/ date = '2008-11-05' user = '1' [ 200, {quot;Content-Typequot; = quot;application/javascriptquot;}, [Pull.by_user(user).for_date(date).map {|i| i.item_id}.to_json]] else [404, {quot;Content-Typequot; = quot;text/htmlquot;}, [quot;Not Foundquot;]] end end end
    50. 50. class Pull ActiveRecord::Base belongs_to :user belongs_to :item named_scope :by_user, lambda { |user_id| {:conditions = {:user_id = user_id}} } named_scope :for_date, lambda { |date| {:include = :item, :conditions = {:items = {:released_on = date}}} } end
    51. 51. $(document).ready(function() { $.getJSON('/pulls', function(data) { $.each(data, function() { $('#item_'+this).addClass('pulled'); }); }); });
    52. 52. standard
    53. 53. hybrid
    54. 54. standard 0.617 hybrid 0.039 0.096 content load time
    55. 55. hybrid approach - version 2
    56. 56. class SessionsController ApplicationController def new; end def create # ... session[:pulls] = @user.pulls.map {|i| i.item_id} redirect_to items_path end end
    57. 57. require(File.dirname(__FILE__) + quot;/../../config/environmentquot;) unless defined?(Rails) class PullList def self.call(env) if env[quot;PATH_INFOquot;] =~ /^/pulls/ date = '2008-11-05' user = '1' [ 200, {quot;Content-Typequot; = quot;application/javascriptquot;}, [env['rack.session'][:pulls].to_json] ] else [404, {quot;Content-Typequot; = quot;text/htmlquot;}, [quot;Not Foundquot;]] end end end
    58. 58. hybrid v2
    59. 59. standard 0.617 hybrid 0.039 0.096 hybrid v2 0.043 0.023 content load time
    60. 60. when to use it
    61. 61. when not to use it
    62. 62. accessibility
    63. 63. accessibility
    64. 64. noscript ul class=quot;nojs usernavquot; li Please a href=quot;/support/quot;enable javascript/a to log in. /li /ul /noscript
    65. 65. Thank You ben sco eld - @bsco eld - http://www.viget.com/extend - http://www.speakerrate.com/bsco eld
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×