This document discusses caching strategies for Rails applications, including:
1. Using Rails caching for queries, pages, assets, and fragments to improve performance.
2. Configuring Cache-Control headers, compression, and CDNs like Fastly for efficient caching.
3. Techniques for caching dynamic content at the edge using surrogate keys and purging cached responses.
6. Query caching
• Automagically done by rails when
perform_caching = true
• Not cached between requests!
• Could just store the query result in a variable
7. Custom Query Caching
class Product < MyActiveModel
def self.latest
Rails.cache.fetch("latest", expires_in: 8.hours) do
Product.last(50)
end
end
end
9. Enable Compression*
Compress all responses with gzip, deflate, etc.
# in application.rb
module FastestAppEver
class Application < Rails::Application
config.middleware.use Rack::Deflater
end
end
* http://robots.thoughtbot.com/content-compression-with-rack-deflater
13. 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'
15. 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”
16. 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”
17. 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)
18. 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”
19. 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
20. 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)!
21. Dynamic Content Caching
• Usually don’t (╯°□°)╯︵ ┻━┻
• Edge Side Includes (ESI)
• Dynamic Site Acceleration (DSA)
22. Fragment Caching
The 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
23. 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 %>
24. 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
25. 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!
27. 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)
28.
29.
30.
31.
32. #cachemoney
• Less requests/bandwidth back to your origin
server
• Avoid complex or less efficient strategies
• Edge Side Includes (ESI)
• Fragment view caching
34. 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
36. 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
42. 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
44. VCL
• Fastly VCL Extensions
• date/time, geoip, hashing, strings, etc.
• Do application logic at the CDN edge
45. URL Rewriting
• Filter bad requests
• Normalize or block paths
• Apache, nginx
• if ($invalid_referer) { return 403; }
• You can do this at the edge!
47. 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?
48. 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
49. 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