accelerating rails with edge caching
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/1.jpg)
Accelerating Rails with edge cachingMichael May | @ohaimmay | SFRails | 10/16/2014
![Page 2: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/2.jpg)
![Page 3: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/3.jpg)
Topic
• Rails caching best practices
• Dynamic content/caching
• Edge caching
• Edge caching dynamic content with Rails
![Page 4: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/4.jpg)
Rails caching
• Query/SQL
• Page/Action (removed from Rails 4 core)
• Asset
• Fragment
![Page 5: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/5.jpg)
config.action_controller.perform_caching = true
Rails caching
![Page 6: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/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: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/7.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/8.jpg)
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://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/9.jpg)
https://fast.mmay.rocks/assets/catzlol-75408509152249b79b818b252da51bc4.png
![Page 10: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/10.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/11.jpg)
![Page 12: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/12.jpg)
![Page 13: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/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: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/14.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/15.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/16.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/17.jpg)
![Page 18: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/18.jpg)
Dynamic Content
![Page 19: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/19.jpg)
Dynamic Content
• Changes are unpredictable!
• user driven events
• Can’t just set a Time To Live (TTL)
![Page 20: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/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: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/21.jpg)
Dynamic Content Caching
• Usually don’t
• Edge Side Includes (ESI)
• Dynamic Site Acceleration (DSA)
![Page 22: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/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.rbdef update … expire_fragment(cache_key_for_products) …end
![Page 23: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/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: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/24.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/25.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/26.jpg)
Edge Caching
![Page 27: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/27.jpg)
Edge Cachingaka content delivery network
aka CDN
![Page 28: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/28.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/29.jpg)
![Page 30: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/30.jpg)
![Page 31: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/31.jpg)
![Page 32: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/32.jpg)
![Page 33: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/33.jpg)
![Page 34: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/34.jpg)
The more content we can offload, the better performance
we get
![Page 35: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/35.jpg)
#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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/36.jpg)
Edge caching the dynamic content
![Page 37: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/37.jpg)
![Page 38: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/38.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 hell out of the pieces of the network we can control
![Page 39: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/39.jpg)
Tagging responses with Surrogate-Key
![Page 40: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/40.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 endend
![Page 41: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/41.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 endend
![Page 42: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/42.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 endend
![Page 43: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/43.jpg)
Purge on updates
![Page 44: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/44.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 45: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/45.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/46.jpg)
fastly-railsgithub.com/fastly/fastly-
rails
![Page 47: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/47.jpg)
Edge caching in practice
![Page 48: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/48.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/49.jpg)
edge scripting
![Page 50: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/50.jpg)
URL Rewriting
• Apache, nginx, etc support URL Rewriting
• Filter bad requests
• Normalize paths
![Page 51: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/51.jpg)
URL Rewrite at the edge
• Varnish HTTP cache
• VCL
• Requests never hit origin!
![Page 52: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/52.jpg)
#winning
meh
yay
![Page 53: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/53.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
• More fine-grained integration with HTTP accelerators like Varnish?
![Page 54: Accelerating Rails with edge caching](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/54.jpg)
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](https://reader034.vdocuments.us/reader034/viewer/2022042713/547e8dd0b379596a2b8b54b8/html5/thumbnails/55.jpg)
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