49
Rails Caching Secrets: From the Edge! Michael May | @ohaimmay | austinonrails | 10/28/2014

Rails Caching: Secrets From the Edge

  • Upload
    fastly

  • View
    381

  • Download
    2

Embed Size (px)

DESCRIPTION

Slides from Fastly engineer Michael May's talk on Rails Caching.

Citation preview

Page 1: Rails Caching: Secrets From the Edge

Rails Caching Secrets: From the Edge!

Michael May | @ohaimmay | austinonrails | 10/28/2014

Page 2: Rails Caching: Secrets From the Edge

We’re Hiring!Distributed Systems! Ruby C Go Perl JS

Page 3: Rails Caching: Secrets From the Edge

We are here

• Rails caching best practices

• Dynamic content

• Edge caching / Content Delivery Networks

• Edge caching dynamic content with Rails

Page 4: Rails Caching: Secrets From the Edge

Rails caching

• Query/SQL

• Page/Action (removed from Rails 4 core!)

• Asset

• Fragment

Page 5: Rails Caching: Secrets From the Edge

config.action_controller.perform_caching = true

Rails caching

Page 6: Rails Caching: Secrets From the Edge

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: Rails Caching: Secrets From the Edge

class Product < MyActiveModel

def self.latest Rails.cache.fetch("latest", expires_in: 8.hours) do Product.last(50) end end

end

Custom Query Caching

Page 8: Rails Caching: Secrets From the Edge

Asset Pipeline• Serve static assets from nginx or apache

• config.serve_static_assets = false

• Enable Compression*

• config.assets.compress = true

• # Rails 4config.assets.css_compressor = :yuiconfig.assets.js_compressor = :uglifier

• Asset Digests

• config.assets.digest = true

Page 9: Rails Caching: Secrets From the Edge

Enable Compression*

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

# in application.rb module FastestAppEver class Application < Rails::Application config.middleware.use Rack::Deflater end end

Compress all responses with gzip, deflate, etc.

Page 10: Rails Caching: Secrets From the Edge

$ curl -sv -H “Accept-Encoding: deflate” \http://fast.mmay.rocks/catz.json

* Connected to fast.mmay.rocks (127.0.0.1) port 666 > GET /catz.json HTTP/1.1 > User-Agent: curl > Host: fast.mmay.rocks:666 > Accept-Encoding: deflate,gzip> < HTTP/1.1 200 OK < Content-Type: application/json; charset=utf-8 < Vary: Accept-Encoding< Content-Encoding: deflate< Cache-Control: max-age=60, s-maxage=3600 < Transfer-Encoding: chunked < * magical encoded bytes* V*.I,)-VRJ-*RN@ ɥEEy%9

Page 11: Rails Caching: Secrets From the Edge
Page 12: Rails Caching: Secrets From the Edge

Before

After

Page 13: Rails Caching: Secrets From the Edge

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: Rails Caching: Secrets From the Edge

HTTP HeadersRFC 2616 Section 14

Page 15: Rails Caching: Secrets From the Edge

Cache-Control HTTP Header“public, maxage=2592000, s-maxage=15552000”

public“please cache me”

maxage=2592000“keep me for 30 days”

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

Page 16: Rails Caching: Secrets From the Edge

Bonus Cache-Control Directives!

• stale-while-revalidate

• Serve the cached (stale) content for n seconds while you re-fetche the new content in the background

• Cache-Control: maxage=604800, stale-while-revalidate=3600

• “Serve stale for up to an hr while you fetch the latest behind the scenes”

• stale-if-error

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

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

• “Serve stale for up to an hr if origin responds with 4xx or 5xx”

Page 17: Rails Caching: Secrets From the Edge

ETags• Automatically added into requests with

Rack::ETag

• Rails renders response every time to calculate etag

• Override default with Conditional GETs

• stale?(@model)

• fresh_when(@model)

Page 18: Rails Caching: Secrets From the Edge

The Vary HTTP Header• Change response base on the value of another

HTTP Header

• Example:“Vary: Accept-Encoding”Accept-Encoding: gzip => Serve Response A Accept-Encoding: deflate => Serve Response B

• “This response changes for different values of the Accept-Encoding header”

Page 19: Rails Caching: Secrets From the Edge

Vary Best Practices

• Please do not Vary on User-Agent

• There are THOUSANDS of these!

• Limits caching benefits - almost Impossible to serve the same response more than once!

• In general, avoid varying on anything other than content encoding

Page 20: Rails Caching: Secrets From the Edge

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: Rails Caching: Secrets From the Edge

Dynamic Content Caching

• Usually don’t (╯°□°)╯︵ ┻━┻

• Edge Side Includes (ESI)

• Dynamic Site Acceleration (DSA)

Page 22: Rails Caching: Secrets From the Edge

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.rb def update … expire_fragment(cache_key_for_products) … end

Page 23: Rails Caching: Secrets From the Edge

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: Rails Caching: Secrets From the Edge

Nested Fragment• Tedious

• Comb through (probably terrible) view code

• Cache keys are weird

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

• But I like “A given key should always return the most up-to-date content” - like a DB primary key

• Hacking around cache limitations

• Memcache and wildcard purging

Page 25: Rails Caching: Secrets From the Edge

Nested Fragment• Garbage left in the cache

• Defaults writing to disk

• 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: Rails Caching: Secrets From the Edge

Edge Cachingwith things like CDNs

Page 27: Rails Caching: Secrets From the Edge

Edge Caches

• Geographically distributed

• Highly optimized storage and network (nanoseconds count)

• Move content physically closer to end-users

• DECREASE LATENCY!(speed of light sux lol)

Page 28: Rails Caching: Secrets From the Edge
Page 29: Rails Caching: Secrets From the Edge
Page 30: Rails Caching: Secrets From the Edge
Page 31: Rails Caching: Secrets From the Edge
Page 32: Rails Caching: Secrets From the Edge

#cachemoney

• Less requests/bandwidth back to your origin server

• Avoid complex or less efficient strategies

• Edge Side Includes (ESI)

• Fragment view caching

Page 33: Rails Caching: Secrets From the Edge

Edge caching dynamic content

Page 34: Rails Caching: Secrets From the Edge

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 pieces of the network we control

Page 35: Rails Caching: Secrets From the Edge

Tagging responses with Surrogate-Keys

Page 36: Rails Caching: Secrets From the Edge

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 end end

Page 37: Rails Caching: Secrets From the Edge

Purge on updates

Page 38: Rails Caching: Secrets From the Edge

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

Page 39: Rails Caching: Secrets From the Edge

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

Page 40: Rails Caching: Secrets From the Edge

fastly-railsgithub.com/fastly/fastly-rails

Page 41: Rails Caching: Secrets From the Edge

Edge caching in practice

Page 42: Rails Caching: Secrets From the Edge

Watch out for Set-Cookie!

• 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 43: Rails Caching: Secrets From the Edge

Edge scripting with VCL(varnish config lang)

Page 44: Rails Caching: Secrets From the Edge

VCL

• Fastly VCL Extensions

• date/time, geoip, hashing, strings, etc.

• Do application logic at the CDN edge

Page 45: Rails Caching: Secrets From the Edge

URL Rewriting

• Filter bad requests

• Normalize or block paths

• Apache, nginx

• if ($invalid_referer) { return 403; }

• You can do this at the edge!

Page 46: Rails Caching: Secrets From the Edge

Synthetic Responses

Page 47: Rails Caching: Secrets From the Edge

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

• Better integration with HTTP accelerators like Varnish?

Page 48: Rails Caching: Secrets From the Edge

Takeaways• Take advantage of Rails built-in caching

• 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 49: Rails Caching: Secrets From the Edge

Questions?

Michael May || @ohaimmay

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