accelerating rails with edge caching

55
Accelerating Rails with edge caching Michael May | @ohaimmay | SFRails | 10/16/2014

Upload: michael-may

Post on 29-Nov-2014

211 views

Category:

Technology


0 download

DESCRIPTION

In this talk, we'll cover all the great built-in rails caching options and best practices for getting the most out of these. Then we'll talk about dynamic content, why it's traditionally not cached, and how you can can cache it using this thing called "edge caching". Welcome to the future, where you can cache the uncacheable.

TRANSCRIPT

Page 1: Accelerating Rails with edge caching

Accelerating Rails with edge cachingMichael May | @ohaimmay | SFRails | 10/16/2014

Page 2: Accelerating Rails with edge caching
Page 3: Accelerating Rails with edge caching

Topic

• Rails caching best practices

• Dynamic content/caching

• Edge caching

• Edge caching dynamic content with Rails

Page 4: Accelerating Rails with edge caching

Rails caching

• Query/SQL

• Page/Action (removed from Rails 4 core)

• Asset

• Fragment

Page 5: Accelerating Rails with edge caching

config.action_controller.perform_caching = true

Rails caching

Page 6: Accelerating Rails with edge caching

Query caching

• Automagically done by rails when perform_caching = true

• Not cached between requests!

• Could just store the query result in a variable

Page 7: Accelerating Rails with edge caching

class Product < MyModel

def self.out_of_stock Rails.cache.fetch("out_of_stock", expires_in: 1.hour) do Product.where("inventory.quantity = 0") end end

end

Manual Query Caching

Page 8: Accelerating Rails with edge caching

Asset Caching• Serve static assets from a proxy

• config.serve_static_assets = false

• Enable Compression*

• config.assets.compress = true

• # In Rails 4

• config.assets.css_compressor = :yui

• config.assets.js_compressor = :uglifier

• Asset Digests

• config.assets.digest = true

Page 9: Accelerating Rails with edge caching

https://fast.mmay.rocks/assets/catzlol-75408509152249b79b818b252da51bc4.png

Page 10: Accelerating Rails with edge caching

Enable Compression*

* http://robots.thoughtbot.com/content-compression-with-rack-deflater

module FastestAppEver

class Application < Rails::Application config.middleware.use Rack::Deflater end

end

Compress HTML, JSON responses at runtime

Page 11: Accelerating Rails with edge caching
Page 12: Accelerating Rails with edge caching
Page 13: Accelerating Rails with edge caching

Asset Caching

• Configure an asset host if needed

• config.action_controller.asset_host = ENV[‘FASTLY_CDN_URL']

• Cache-Control like a pro

• config.static_cache_control = 'public, s-maxage=15552000, maxage=2592000'

Page 14: Accelerating Rails with edge caching

Cache-Controlpublic, s-maxage=15552000, maxage=2592000

public“please cache me”

maxage=2592000“keep me for 30 days”

s-maxage=15552000“PROXIES ONLY! - Keep me for 180 days”

Page 15: Accelerating Rails with edge caching

Keepin’ it fresh• stale-while-revalidate

• Serve the current (stale) version for n seconds while it re-fetches the latest version in the background

• Cache-Control: max-age=604800, stale-while-revalidate=86400

• stale-if-error

• If the re-fetch fails within n seconds of the response becoming stale, serve the cached response

• Cache-Control: max-age=604800, stale-while-revalidate=86400, stale-if-error=259200

Page 16: Accelerating Rails with edge caching

The Vary HTTP Header

• In general, never Vary on anything other than Content-Encoding

• Varying makes it impossible to serve the same response more than once and limits caching benefits

• NEVER Vary on User-Agent!

• There are THOUSANDS of these!

Page 17: Accelerating Rails with edge caching
Page 18: Accelerating Rails with edge caching

Dynamic Content

Page 19: Accelerating Rails with edge caching

Dynamic Content

• Changes are unpredictable!

• user driven events

• Can’t just set a Time To Live (TTL)

Page 20: Accelerating Rails with edge caching

Dynamic Content

• Changes are unpredictable!

• user driven events

• Can’t just set a Time To Live (TTL)

• Frequently, but not continuously changing

• Actually static for short periods of time (we can cache static things)!

Page 21: Accelerating Rails with edge caching

Dynamic Content Caching

• Usually don’t

• Edge Side Includes (ESI)

• Dynamic Site Acceleration (DSA)

Page 22: Accelerating Rails with edge caching

Fragment CachingThe rails answer to caching dynamic

HTML# products/index.html.erb<% cache(cache_key_for_products) do %> <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %> <% end %>

# products_controller.rbdef update … expire_fragment(cache_key_for_products) …end

Page 23: Accelerating Rails with edge caching

Nested Fragment Caching

<% cache(cache_key_for_products) do %> All available products: <% Product.all.each do |p| %>

<% cache(p) do %> <%= link_to p.name, product_url(p) %> <% end %>

<% end %><% end %>

Page 24: Accelerating Rails with edge caching

Nested Fragment Issues

• Tedious

• Comb through (probably terrible) view code

• Cache keys are weird

• “A given key should always return the same content.” - DHH

• products/15-20110218104500

• “A given key should always return the most up-to-date content.” - Me

• products/15

• Hacks around cache limitations

• Memcache has no wildcard purging!

Page 25: Accelerating Rails with edge caching

Nested Fragment Issues

• Garbage left in the cache

• Defaults writing to disk

• Memcached, Redis, etc

• Probably lives in the same DC as your app server

• Distributing, replication takes effort

• What about dynamic API caching?

• “The caching itself happens in the views based on partials rendering the objects in question”

• Take control over your cached data!

Page 26: Accelerating Rails with edge caching

Edge Caching

Page 27: Accelerating Rails with edge caching

Edge Cachingaka content delivery network

aka CDN

Page 28: Accelerating Rails with edge caching

Edge Cache

• Geographically distributed

• Highly optimized storage and network (nanoseconds count)

• Move content physically closer to the end-users

• End goal - DECREASE LATENCY!

Page 29: Accelerating Rails with edge caching
Page 30: Accelerating Rails with edge caching
Page 31: Accelerating Rails with edge caching
Page 32: Accelerating Rails with edge caching
Page 33: Accelerating Rails with edge caching
Page 34: Accelerating Rails with edge caching

The more content we can offload, the better performance

we get

Page 35: Accelerating Rails with edge caching

#cachemoney• App servers cost real cash money (not

cache money)

• Less requests back to your application server

• Avoid complex or less efficient strategies

• Edge Side Includes (ESI)

• Fragment caching

Page 36: Accelerating Rails with edge caching

Edge caching the dynamic content

Page 37: Accelerating Rails with edge caching
Page 38: Accelerating Rails with edge caching

Our approach to dynamic content

• Tag content with Surrogate-Key HTTP headers

• Programmatically purge (~150ms globally)

• By Surrogate-Key

• By resource path

• Real-time analytics and log streaming

• Optimize the hell out of the pieces of the network we can control

Page 39: Accelerating Rails with edge caching

Tagging responses with Surrogate-Key

Page 40: Accelerating Rails with edge caching

class ProductsController < ApplicationController # set Cache-Control, strip Set-Cookie before_filter :set_cache_control_headers,only [:index,:show] def index @products = Product.last(10) # set Surrogate-Key: products set_surrogate_key_header @products.table_key respond_with @products end def show @product = Products.find(params[:id]) # set Surrogate-Key: product/666 set_surrogate_key_header @product.record_key respond_with @product endend

Page 41: Accelerating Rails with edge caching

class ProductsController < ApplicationController # set Cache-Control, strip Set-Cookie before_filter :set_cache_control_headers,only [:index,:show] def index @products = Product.last(10) # set Surrogate-Key: products set_surrogate_key_header @products.table_key respond_with @products end def show @product = Products.find(params[:id]) # set Surrogate-Key: product/666 set_surrogate_key_header @product.record_key respond_with @product endend

Page 42: Accelerating Rails with edge caching

class ProductsController < ApplicationController # set Cache-Control, strip Set-Cookie before_filter :set_cache_control_headers,only [:index,:show] def index @products = Product.last(10) # set Surrogate-Key: products set_surrogate_key_header @products.table_key respond_with @products end def show @product = Products.find(params[:id]) # set Surrogate-Key: product/666 set_surrogate_key_header @product.record_key respond_with @product endend

Page 43: Accelerating Rails with edge caching

Purge on updates

Page 44: Accelerating Rails with edge caching

class ProductsController < ApplicationController def create @product = Product.new(params) if @product.save # purge Surrogate-Key: products @product.purge_all render @product end end ...

Page 45: Accelerating Rails with edge caching

def update @product = Product.find(params[:id]) if @product.update(params) # purge Surrogate-Key: product/666 @product.purge render @product endend

Page 46: Accelerating Rails with edge caching

fastly-railsgithub.com/fastly/fastly-

rails

Page 47: Accelerating Rails with edge caching

Edge caching in practice

Page 48: Accelerating Rails with edge caching

Be aware of cookies

• Nothing with a Set-Cookie header is cached (by default)

• Authentication frameworks/middleware might inject Set-Cookie after the rails stack removes it

• Avoid caching pains by knowing when, where, and how you use Set-Cookie

Page 49: Accelerating Rails with edge caching

edge scripting

Page 50: Accelerating Rails with edge caching

URL Rewriting

• Apache, nginx, etc support URL Rewriting

• Filter bad requests

• Normalize paths

Page 51: Accelerating Rails with edge caching

URL Rewrite at the edge

• Varnish HTTP cache

• VCL

• Requests never hit origin!

Page 52: Accelerating Rails with edge caching

#winning

meh

yay

Page 53: Accelerating Rails with edge caching

What can we do better?

• Add better caching defaults?

• Cache-Control, stale-while-revalidate, stale-if-error

• Re-use existing rails cache interfaces for edge caching?

• ActiveSupport::Cache::EdgeStore

• More fine-grained integration with HTTP accelerators like Varnish?

Page 54: Accelerating Rails with edge caching

Takeaways• Rails has tons of built-in caching options

• Get fancy with Cache-Control directives

• Use Google PageSpeed Insights (chrome plugin adds it to dev tools)

• Dynamic edge caching is all about the power of purge!

• Similar school of thought to rails action caching

Page 55: Accelerating Rails with edge caching

Questions?

Contact: Michael May | @ohaimmay | [email protected]

cool links:fastly-rails - github.com/fastly/fastly-railssurrogate keys - fastly.com/blog/surrogate-keys-part-1cache-control tutorial - docs.fastly.com/guides/tutorials/cache-control-tutorialserve stale cache-control - fastly.com/blog/stale-while-revalidatevary header best practices - fastly.com/blog/best-practices-for-using-the-vary-headercaching like & share buttons - fastly.com/blog/caching-like-and-share-buttonspagespeed insights - developers.google.com/speed/pagespeed