Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Page Caching Resurrected

3,897 views

Published on

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

Published in: Lifestyle, Technology
  • Be the first to comment

Page Caching Resurrected

  1. Page Caching Resurrected Ben Sco eld – Viget Labs
  2. Page Caching Resurrected: A Fairy Tale Ben Sco eld – Viget Labs
  3. three little pigs
  4. three little pigs
  5. page caching
  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. 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. 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. standard 1 page 3000 requests per second
  10. public dynamic content
  11. action caching
  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. 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. 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. standard 1 action 500 page 3000 requests per second
  16. dynamic content
  17. fragment caching
  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. <% 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. class PigsController # ... def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_fragment :key => 'index:piglist' end end
  21. class PigsController # ... def update @pig = Pig.find(params[:id]) @pig.update_attributes(params[:pig]) expire_fragment :key => 'index:piglist' end end
  22. standard 1 fragment 20 action 500 page 3000 requests per second
  23. eficiency
  24. the idea
  25. Rails 2.3
  26. hello, metal
  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. high-performance endpoints
  29. AJAX
  30. so...
  31. javascript
  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. 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. 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. noscript ul class=quot;nojs usernavquot; li Please a href=quot;/support/quot;enable javascript/a to log in. /li /ul /noscript
  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. 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. 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. hulu is insane don’t do it like they do
  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. 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. the* right way *a
  43. the standard approach
  44. class ItemsController ApplicationController def index @user = User.find_by_login(params[:user]) @items = Item.released_on(params[:date]) end end
  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. 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. the hybrid approach
  48. class ItemsController ApplicationController caches_page :index def index @items = Item.released_on(params[:date]).all end end
  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. 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. $(document).ready(function() { $.getJSON('/pulls', function(data) { $.each(data, function() { $('#item_'+this).addClass('pulled'); }); }); });
  52. standard
  53. hybrid
  54. standard 0.617 hybrid 0.039 0.096 content load time
  55. hybrid approach - version 2
  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. 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. hybrid v2
  59. standard 0.617 hybrid 0.039 0.096 hybrid v2 0.043 0.023 content load time
  60. when to use it
  61. when not to use it
  62. accessibility
  63. accessibility
  64. noscript ul class=quot;nojs usernavquot; li Please a href=quot;/support/quot;enable javascript/a to log in. /li /ul /noscript
  65. Thank You ben sco eld - @bsco eld - http://www.viget.com/extend - http://www.speakerrate.com/bsco eld

×