rails caching secrets from the edge
DESCRIPTION
When it comes to caching, there are two types of web developers - those with phat stacks of cache money and those suffering from cache anxiety. Caching is particularly handy when scaling Rails apps, however we often avoid putting in effort because it can quickly get complicated without effective strategies. Rails provides a host of built-in caching interfaces that are easy to leverage and extend. I’ll talk about how to do this and combine rails with technologies like CDNs and HTTP accelerators like Varnish so that you can more effectively cache everything, everywhere without fear of serving stale content. Michael May is an API Engineer at Fastly and a former Austinite, now hailing from San Francisco. While in Texas he studied at UT Austin and co-founded CDN Sumo, which was acquired by Fastly. He’s waiting for the day when FaaS (Franklin BBQ as a Service) becomes a thing and dreams about fast websites.TRANSCRIPT
![Page 1: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/1.jpg)
Rails Caching Secrets: From the Edge!
Michael May | @ohaimmay | austinonrails | 10/28/2014
![Page 2: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/2.jpg)
We’re Hiring!Distributed Systems! Ruby C Go Perl JS
![Page 3: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/3.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/4.jpg)
Rails caching
• Query/SQL
• Page/Action (removed from Rails 4 core!)
• Asset
• Fragment
![Page 5: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/5.jpg)
config.action_controller.perform_caching = true
Rails caching
![Page 6: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/6.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/7.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/8.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/9.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/10.jpg)
$ 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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/11.jpg)
![Page 12: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/12.jpg)
Before
After
![Page 13: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/13.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/14.jpg)
HTTP HeadersRFC 2616 Section 14
![Page 15: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/15.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/16.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/17.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/18.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/19.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/20.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/21.jpg)
Dynamic Content Caching
• Usually don’t (╯°□°)╯︵ ┻━┻
• Edge Side Includes (ESI)
• Dynamic Site Acceleration (DSA)
![Page 22: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/22.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/23.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/24.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/25.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/26.jpg)
Edge Cachingwith things like CDNs
![Page 27: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/27.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/28.jpg)
![Page 29: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/29.jpg)
![Page 30: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/30.jpg)
![Page 31: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/31.jpg)
![Page 32: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/32.jpg)
#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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/33.jpg)
Edge caching dynamic content
![Page 34: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/34.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/35.jpg)
Tagging responses with Surrogate-Keys
![Page 36: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/36.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/37.jpg)
Purge on updates
![Page 38: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/38.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/39.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/40.jpg)
fastly-railsgithub.com/fastly/fastly-rails
![Page 41: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/41.jpg)
Edge caching in practice
![Page 42: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/42.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/43.jpg)
Edge scripting with VCL(varnish config lang)
![Page 44: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/44.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/45.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/46.jpg)
Synthetic Responses
![Page 47: Rails Caching Secrets from the Edge](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/47.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/48.jpg)
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](https://reader033.vdocuments.us/reader033/viewer/2022052908/55945b4d1a28ab190c8b45e8/html5/thumbnails/49.jpg)
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