Slide 1

Slide 1 text

Rails Caching Strategies Benjamin Curtis Wednesday, December 5, 12

Slide 2

Slide 2 text

Wednesday, December 5, 12

Slide 3

Slide 3 text

Who am I? • Playing with Rails since 0.11 • Working with Rails since 2005 • Founder of RailsKits, and co-founder of Honeybadger.io Wednesday, December 5, 12

Slide 4

Slide 4 text

The Official Word • Page, action, and fragment caching • Data caching • Conditional GET http://guides.rubyonrails.org/caching_with_rails.html Wednesday, December 5, 12

Slide 5

Slide 5 text

Three Scenarios • Read-heavy site • Write-heavy site • Interactive app (both read and write) Wednesday, December 5, 12

Slide 6

Slide 6 text

Lots of Reads • Conditional GET • Data caching • Proxy Wednesday, December 5, 12

Slide 7

Slide 7 text

Wednesday, December 5, 12

Slide 8

Slide 8 text

module CacheController protected def uncache_it headers['Cache-Control'] = 'max-age=0, private, must-revalidate' end def cache_it(max_age = 30) headers['Vary'] = 'Accept' expires_in max_age, :public => true end def etag_it(is_public = true) headers['Vary'] = 'Accept' fresh_when(:last_modified => resource.updated_at.utc, :etag => resource, :public => is_public) end end Wednesday, December 5, 12

Slide 9

Slide 9 text

http { if_modified_since before; } server { location @app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://<%= @app[:name] %>; proxy_cache <%= @app[:name] %>; proxy_cache_key "$scheme$host$request_uri"; proxy_cache_valid 5m; proxy_cache_use_stale error timeout updating invalid_header http_500 http_502 http_504; proxy_max_temp_file_size 1M; } } Wednesday, December 5, 12

Slide 10

Slide 10 text

def walk_score Rails.cache.fetch("walkscore/#{cache_key}") do JSON.parse(open("http://api.walkscore.com/score?format=json...").read) end end Wednesday, December 5, 12

Slide 11

Slide 11 text

Lots of Writes • Data caching • Background jobs Wednesday, December 5, 12

Slide 12

Slide 12 text

Wednesday, December 5, 12

Slide 13

Slide 13 text

def valid_project(token) r = Redis.current project = nil unless project_id = r.get("pass:#{token}") deny_request(token, 'Invalid API key') and return false unless project = Project.find_by_token(token) end # Skip token and active checks for the next hour if project r.pipelined do r.set("pass:#{token}", project.id) r.expire("pass:#{token}", 3600) end end project_id || project.id end Wednesday, December 5, 12

Slide 14

Slide 14 text

Lots of Everything • Use JS (single-page app, pjax, rjs, ajax) • Conditional GET Wednesday, December 5, 12