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.
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. Manual Query 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
10. Enable Compression*
Compress HTML, JSON responses at runtime
module FastestAppEver
class Application < Rails::Application
config.middleware.use Rack::Deflater
end
* http://robots.thoughtbot.com/content-compression-with-rack-deflater
end
11.
12.
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'
14. Cache-Control
public, 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”
15. 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
16. 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!
19. Dynamic Content
• Changes are unpredictable!
• user driven events
• Can’t just set a Time To Live (TTL)
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 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!
25. 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!
35. #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
38. 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
40. 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
41. 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. 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
48. 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
53. 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?
54. 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
55. Questions?
Contact: Michael May | @ohaimmay | michael@fastly.com
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