When to tell your kids about presentation caching Matthew Deiters - - PowerPoint PPT Presentation

when to tell your kids about presentation caching
SMART_READER_LITE
LIVE PREVIEW

When to tell your kids about presentation caching Matthew Deiters - - PowerPoint PPT Presentation

When to tell your kids about presentation caching Matthew Deiters www.theAgileDeveloper.com A practical guide to stuffing your apps bits into someone elses browser Questions: @mdeiters Rapid Feature Development Rapid Feature


slide-1
SLIDE 1

When to tell your kids about presentation caching

Matthew Deiters www.theAgileDeveloper.com

slide-2
SLIDE 2

A practical guide to stuffing your app’s bits into someone else’s browser

slide-3
SLIDE 3

Questions: @mdeiters

slide-4
SLIDE 4
slide-5
SLIDE 5

Rapid Feature Development

slide-6
SLIDE 6

Rapid Feature Development Adoption & Growth

slide-7
SLIDE 7
slide-8
SLIDE 8

Client Caching

slide-9
SLIDE 9

client.is_a?(Browser) == true

slide-10
SLIDE 10

Browsers & Leveraging HTTP 1.1

slide-11
SLIDE 11

Fewer Requests Smaller Responses

slide-12
SLIDE 12

80/20 Rule (Pareto Principle)

slide-13
SLIDE 13

80% of the wealth owned by 20% of people

slide-14
SLIDE 14

80% of your time is with 20% of your acquaintances

slide-15
SLIDE 15

80% of the time you wear 20% of your clothing

slide-16
SLIDE 16

80% of a request is spent on the wire

slide-17
SLIDE 17

Today

slide-18
SLIDE 18

Last-Modified Header

ETag Header

max-age Header

Expires Header

Reducing Network Trac

GZip Minification

Cookies

Today

slide-19
SLIDE 19
slide-20
SLIDE 20
slide-21
SLIDE 21

ME

slide-22
SLIDE 22
slide-23
SLIDE 23
slide-24
SLIDE 24
slide-25
SLIDE 25

To illustrate: Scalability

slide-26
SLIDE 26
slide-27
SLIDE 27
slide-28
SLIDE 28
slide-29
SLIDE 29
slide-30
SLIDE 30

Applicable for?

slide-31
SLIDE 31

Applicable for?

Enterprises

slide-32
SLIDE 32

Applicable for?

Enterprises High Traffic Web Sites

slide-33
SLIDE 33

Applicable for?

Enterprises High Traffic Web Sites Startups

slide-34
SLIDE 34

Enterprise

slide-35
SLIDE 35

High Traffic Sites

slide-36
SLIDE 36

Reduce network traffic Reduce response times Reduce load

slide-37
SLIDE 37

Facebook: Bumpersticker

slide-38
SLIDE 38

1.4 Million Average Users

slide-39
SLIDE 39

1.4 Million Average Users Average 20 page views

slide-40
SLIDE 40

“Push everything you possibly can to the client to reduce the amount of traffic going over the network...”

slide-41
SLIDE 41

Startups

slide-42
SLIDE 42
slide-43
SLIDE 43

HTTP 1.1 Enity Tags

slide-44
SLIDE 44
slide-45
SLIDE 45
slide-46
SLIDE 46

www.nextdaypets.com

slide-47
SLIDE 47

\puppies\43

slide-48
SLIDE 48

HTTP/1.x 200 OK Etag: "8b2242293d5e5b02e99b3be73fc0c9fa"

slide-49
SLIDE 49
slide-50
SLIDE 50
slide-51
SLIDE 51

www.nextdaypets.com

slide-52
SLIDE 52

If-None-Match: "8b2242293d5e5b02e99b3be73fc0c9fa"

\puppies\43

slide-53
SLIDE 53

HTTP/1.x 304 Not Modified

slide-54
SLIDE 54
slide-55
SLIDE 55
slide-56
SLIDE 56

Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT ETag: "10c24bc-4ab-457e1c1f"

slide-57
SLIDE 57

Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT ETag: "10c24bc-4ab-457e1c1f" If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT If-None-Match: "10c24bc-4ab-457e1c1f"

slide-58
SLIDE 58

+ Conditional Get

slide-59
SLIDE 59

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) respond_to do |wants| #... end end end

slide-60
SLIDE 60

#response.rb def last_modified=(utc_time) def etag=(etag)

slide-61
SLIDE 61

#request.rb def fresh?(response) def not_modified?(modified_at) def etag_matches?(etag)

slide-62
SLIDE 62

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) respond_to do |wants| #... end end end

slide-63
SLIDE 63

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) respond_to do |wants| #... end end end response.last_modified = @person.updated_at.utc

slide-64
SLIDE 64

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) respond_to do |wants| #... end end end response.last_modified = @person.updated_at.utc response.etag = @person

slide-65
SLIDE 65

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) respond_to do |wants| #... end end end return head(:not_modified) if request.fresh?(response) response.last_modified = @person.updated_at.utc response.etag = @person

slide-66
SLIDE 66

response.etag = @person # => “5cb44721b6ce18857ff6900486dc4aba” @person.cache_key # => "people/5-20071224150000"

slide-67
SLIDE 67

def fresh_when(options) def stale?(options)

slide-68
SLIDE 68

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) response.last_modified = @person.updated_at.utc response.etag = @person return head(:not_modified) if request.fresh?(response) respond_to do |wants| #... end end end

slide-69
SLIDE 69

class PeopleController < ApplicationController def show @person = Person.find(params[:id]) if stale?(:etag => @person, :last_modified => @person.updated_at.utc) respond_to do |wants| #... end end end end

slide-70
SLIDE 70

Last-Modified vs ETag

slide-71
SLIDE 71
slide-72
SLIDE 72
slide-73
SLIDE 73

response.etag = [@admin, @person, flash]

Later

slide-74
SLIDE 74

def handle_conditional_get! if nonempty_ok_response? self.etag ||= body if request && request.etag_matches?(etag) self.status = '304 Not Modified' self.body = '' end end set_conditional_cache_control! if etag? || last_modified? end

slide-75
SLIDE 75

self.etag ||= body

SInce Feb 2007

slide-76
SLIDE 76
slide-77
SLIDE 77

http://localhost:3000/people GET /people HTTP/1.1

slide-78
SLIDE 78

http://localhost:3000/people GET /people HTTP/1.1 HTTP/1.x 200 OK ... Etag: "94785662c6f60cb96681ed1b09a44783"

slide-79
SLIDE 79

http://localhost:3000/people GET /people HTTP/1.1 HTTP/1.x 200 OK ... Etag: "94785662c6f60cb96681ed1b09a44783" http://localhost:3000/people GET /people HTTP/1.1 If-None-Match: "94785662c6f60cb96681ed1b09a44783"

slide-80
SLIDE 80

http://localhost:3000/people GET /people HTTP/1.1 HTTP/1.x 200 OK ... Etag: "94785662c6f60cb96681ed1b09a44783" http://localhost:3000/people GET /people HTTP/1.1 If-None-Match: "94785662c6f60cb96681ed1b09a44783" HTTP/1.x 304 Not Modified Etag: "94785662c6f60cb96681ed1b09a44783"

slide-81
SLIDE 81

send_file

slide-82
SLIDE 82
slide-83
SLIDE 83

Assets

slide-84
SLIDE 84

Assets

slide-85
SLIDE 85

Assets

slide-86
SLIDE 86

INODE

slide-87
SLIDE 87

/intl/en_ALL/images/logo.gif ETag: "48b6a5bf-47f4-a0757"

slide-88
SLIDE 88

/intl/en_ALL/images/logo.gif ETag: "48b6a5bf-47f4-a0757" /intl/en_ALL/images/logo.gif ETag: "48b6a5bf-61a-21c86a4"

slide-89
SLIDE 89

/intl/en_ALL/images/logo.gif ETag: "48b6a5bf-47f4-a0757" /intl/en_ALL/images/logo.gif ETag: "48b6a5bf-61a-21c86a4"

slide-90
SLIDE 90

Avoid Cache Expiration & Validation

slide-91
SLIDE 91

/stylesheets/screen.css?1219926880

CACHE BUSTER!

slide-92
SLIDE 92

<FilesMatch "\.(pdf|flv|jpg|jpeg|png|gif|js|css|swf)$"> Header set Cache-Control "public" ExpiresActive On ExpiresDefault “access plus 10 years” FileETag None Header unset Last-Modified Header unset ETag </FilesMatch>

Now

slide-93
SLIDE 93

/images/beach.png?1241477547 /images/beach.png?1241477554 Server 1 Server 2

slide-94
SLIDE 94

Option 1

#config/environments/production.rb #Subversion ENV['RAILS_ASSET_ID'] = YAML::load(`svn info $RAILS_ROOT`)["Revision"].to_i #GIT (Check out Grit too) ENV['RAILS_ASSET_ID'] = File.read(RAILS_ROOT + '/.git/refs/heads/deploy').chomp

slide-95
SLIDE 95

Option 2

task :finalize_update, :except => { :no_release => true } do stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S") asset_paths = %w(images stylesheets javascripts).map do |asset| "#{latest_release}/public/#{p}" end.join(" ") run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" } end

slide-96
SLIDE 96

Option 2

#Capistrano 2.4 set :normalize_asset_timestamps, true

slide-97
SLIDE 97

Option 3

slide-98
SLIDE 98

use-commit-times

Option 3

slide-99
SLIDE 99

Now

ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = true

slide-100
SLIDE 100

Proxies & Via Header

slide-101
SLIDE 101

/stylesheets/screen.css?1219926880 /stylesheets/screen.1219926880.css

Bag of Tricks

slide-102
SLIDE 102

#asset_tag_helper.rb def rewrite_asset_path(source) #... source + "?#{asset_id}" end

slide-103
SLIDE 103

#Rules for Versioned Static Files RewriteRule ^(scripts|css|images)/(.+)\.(.+)\.(js|css|jpg|gif|png)$ $1/$2.$4 [L]

slide-104
SLIDE 104
slide-105
SLIDE 105

AJAX

slide-106
SLIDE 106

DUMB-ASSES

slide-107
SLIDE 107
slide-108
SLIDE 108

:method => :post

slide-109
SLIDE 109

:method => :post

slide-110
SLIDE 110

Superfluous values in URL

slide-111
SLIDE 111

Superfluous values in URL

slide-112
SLIDE 112

HTTP Headers

slide-113
SLIDE 113

HTTP Headers

slide-114
SLIDE 114

headers['Last-Modified'] = Time.now.httpdate headers['Expires'] = '-1' headers['Pragma'] = 'no-cache' headers['Cache-Control'] = 'no-cache, must-revalidate, max-age=0, pre-check=0, post-check=0'

slide-115
SLIDE 115
slide-116
SLIDE 116

#http://github.com/dancroak/no_cache no_cache :first_name_autocomplete, :index

Now

slide-117
SLIDE 117

Cache Ajax?

slide-118
SLIDE 118

Speed up rendering

slide-119
SLIDE 119

default 2 connections per host for HTTP 1.1 connections

slide-120
SLIDE 120

ActionController::Base.asset_host = "http://mt%d.google.com"

slide-121
SLIDE 121

ActionController::Base.asset_host = "http://mt%d.google.com" http://mt0.google.com http://mt1.google.com http://mt2.google.com http://mt3.google.com

slide-122
SLIDE 122

http://mt0.google.com http://mt1.google.com http://mt2.google.com http://mt3.google.com

CNAME

Now

slide-123
SLIDE 123

greater than 40% drop in page load time

slide-124
SLIDE 124

host_names >= 2 && host_names <= 4

slide-125
SLIDE 125

# http://github.com/dhh/asset-hosting-with-minimum-ssl

config.action_controller.asset_host = AssetHostingWithMinimumSsl.new( # will serve non-SSL assetts on http://assets[1-4].example.com "http://assets%d.example.com", # will serve SSL assets on https://assets1.example.com "https://assets1.example.com" )

Now

slide-126
SLIDE 126

Less requests are better

slide-127
SLIDE 127

ActionController::Base.perform_caching = true

Combine Assets

slide-128
SLIDE 128

<%= javascript_include_tag 'application', 'user' %>

<script src="/javascripts/application.js?1219633350" type="text/javascript"></script> <script src="/javascripts/user.js?1219633368" type="text/javascript"></script>

slide-129
SLIDE 129

<%= javascript_include_tag 'application', 'user', :cache => :true %>

<script src="/javascripts/all.js?1219633651" type="text/javascript"></script>

slide-130
SLIDE 130

<%= javascript_include_tag 'application', 'user', :cache=>‘login’ %>

<script src="/javascripts/login.js?1219633651" type="text/javascript"></script>

slide-131
SLIDE 131

application.js user.js

+

login.js

slide-132
SLIDE 132
slide-133
SLIDE 133

Compiles on Server

Dedicated Asset Server / CDN Not all servers may have login.js

slide-134
SLIDE 134

Timestamp Conflict

<script src="/javascripts/all.js?1219633651" type="text/javascript"></script> <script src="/javascripts/all.js?1219634734" type="text/javascript"></script>

slide-135
SLIDE 135

asset_packager || cramjam || CSSJSC

slide-136
SLIDE 136

Minify

slide-137
SLIDE 137

JSMIN || PackR

CSMIN

Smurf

rucksack

minified_cache

YUI Compressor

http://compressorrater.thruhere.net/ Now

slide-138
SLIDE 138

bundle_fu

slide-139
SLIDE 139

<% bundle do %> <%= javascript_include_tag "prototype" %> <%= stylesheet_link_tag "basic.css" %> <%= calendar_date_select_includes "red" %> <script src="javascripts/application.js" type="text/javascript"></script> <% end %>

<script src="/javascripts/cache/bundle.js?1220211999" type="text/javascript"></script>

slide-140
SLIDE 140

Generates files locally during development

slide-141
SLIDE 141
slide-142
SLIDE 142

www.example.org dynamic content static.example.org components (cookies don’t go here)

slide-143
SLIDE 143

www.example.org dynamic content static.example.org components (cookies don’t go here) Now

slide-144
SLIDE 144

CDN & Asset Servers

slide-145
SLIDE 145

Let Google be your CDN

slide-146
SLIDE 146
slide-147
SLIDE 147
slide-148
SLIDE 148
slide-149
SLIDE 149
slide-150
SLIDE 150
slide-151
SLIDE 151

Google Ajax Libraries

slide-152
SLIDE 152

!"#$%& '%()()&'$ *+%,')-.+#/(-#* 0((1((/* 2(!(

slide-153
SLIDE 153

“Once we host a release of a given library, we are committed to hosting that release indefinitely”

slide-154
SLIDE 154

Last-Modified: Fri, 30 May 2008 06:03:19 GMT Expires: Sun, 17 Jan 2038 19:14:07 GMT Cache-Control: public Date: Sat, 30 Aug 2008 20:10:26 GMT

slide-155
SLIDE 155

Cache-Control: public

slide-156
SLIDE 156
slide-157
SLIDE 157

mod_deflate vs mod_gzip

slide-158
SLIDE 158

mod_deflate vs mod_gzip

Baked w/ Apache Easier on CPU ~35% Compresion

slide-159
SLIDE 159

mod_deflate vs mod_gzip

Baked w/ Apache Easier on CPU ~35% Compresion ~29% Compresion

slide-160
SLIDE 160

config.middleware.use Rack::Deflater

slide-161
SLIDE 161

GZip + Minification + Minimum Components

slide-162
SLIDE 162

Fewer Requests Smaller Responses

slide-163
SLIDE 163

There is more potential for improvement by focusing on the front-end. Cutting it in half reduces response times by 40% or more, whereas cutting back-end performance in half results in less than a 10% reduction.

slide-164
SLIDE 164

Front-end improvements typically require less time and resources than back-end projects (redesigning application architecture and code, finding and optimizing critical code paths, adding or modifying hardware, distributing databases, etc.).

slide-165
SLIDE 165

yslow LiveHTTPHeaders Fiddler http://compressorrater.thruhere.net/

slide-166
SLIDE 166

Matthew Deiters www.theAgileDeveloper.com