Advanced Caching in Rails

94378c403019af23a28b08447a34b8e0?s=47 Adam Hawkins
September 21, 2012

Advanced Caching in Rails

Presented at Frozen Rails in Helsinki 2012

94378c403019af23a28b08447a34b8e0?s=128

Adam Hawkins

September 21, 2012
Tweet

Transcript

  1. Advanced Caching in Rails Presented with love in Helsinki for

    Frozen Rails 2012
  2. Tervetuloa! • Olen Adam Hawkins • @adman65 • gh://twinturbo •

    broadcastingadam.com • https://speakerdeck.com/u/twinturbo • Lead code money at Radium
  3. You May Have Read This

  4. Zero to Sixty

  5. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag

  6. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag 200 or

    304 • Cache-Control • ETag • If-None-Match • If-Modified-Since • Protocol level caching -- take advantage!
  7. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag Middleware to

    support HTTP Caching
  8. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Follows

    HTTP caching rules • Respect freshness • Stores cacheable content in it’s own store • Forwards request down the stack
  9. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Adds

    a 304 Not Modified header • Continues the request based on freshness
  10. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Adds

    an ETag header if applicable • ETag is hash of the body
  11. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag LOL, No

  12. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Captures

    body and headers • Dump it in Rails.cache • Like HTTP caching, but worse
  13. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Traditionally

    used to cache HTML • Only usable in views
  14. HTTP Page Action Fragment Rails.cache Rack::Cache Rack::ConditionalGet Rack::ETag • Writes

    content to the store • Usually Memcache • Actions and Fragments are built on this • Can cache whatever you like • Use it
  15. # HTTP Caching def show # 304 if fresh #

    else respond_with if stale? @post respond_with @post end end
  16. # Action Caching class PostsController < ApplicationController caches_action :index end

  17. # Fragment Caching <% cache 'posts' do %> <!-- HTML

    HERE --> <% end %>
  18. # Rails.cache Rails.cache.write("hash", {:this => :hash}) Rails.cache.write "foo", "bar" Rails.cache.read

    "foo" Rails.cache.read "hash" Rails.cache.read "foo"
  19. What do I use? • Rails.cache when you want to

    cache arbitrary computations (anywhere in your code!) • HTTP caching everywhere • Fragment caching for HTML • Action and page caching, not so much
  20. Hyvä, we can cache things

  21. Perkele! Now we to expire things

  22. None
  23. None
  24. “There are only two hard problems in Computer Science: GNU/Linux

    and cache invalidation” - RMS paraphrasing Phil Karlton
  25. Why So Hard? • Data is usually associated or dependent

    • Data is usually used to compose views • Output depend on different parts of data • Data can change in many places • Not all changes happen during HTTP requests (worker queues) • This is actually really hard
  26. Approaches • Dependency graphs • Auto-expiring cache keys • Manual

    expiration (puke) • Break cacheable content into smaller discrete chunks
  27. Dependency Graphs • The most complex thing that could possibly

    work • Track associations • Walk the graph to connected nodes when one node changes. Expiring nodes as you go along • gh://jcoglan/primer
  28. Manual Expiration • Rails.cache.delete “foo” • Sweepers -- seriously who

    use these? • Extremely complex because you must know every possible cached thing ever • Waste of time, don’t do it
  29. Auto-expiring Keys • The easiest thing that could possibly work

    • No need to manually expire • Cache key represents object state • Includes a hash or timestamp • PROTIP: reduce data to smallest unit where changes can be tracked.
  30. /posts/1/12348971239487 updated_at or last modified date

  31. Damn, that was easy

  32. None
  33. Use Cases

  34. # Caching Collections class ActiveRecord::Relation def cache_key scoped.maximum(:updated_at).to_i end end

    # In your view for fragments cache @posts # for HTTP caching fresh? @posts
  35. • Cache one thing that caches another • Simple example:

    a cache list of posts. Each post is cached as well. List of posts uses a composite cache key. Expiring one post expires the list. Rerendering the list only needs the updated post. Everything else is still cached. • Magically coming to Rails 4* Russian Doll Caching
  36. <%# _posts.erb %> <%# the _posts partial gets linked to

    the _post partial %> <%# so a change there will expire this partial %> <%= render :partial => "posts" %> <%# _post.erb %> <%= post.title %> <%# you get some crazy cache key %> views/posts/065816632-20120810191209/d946707c67ab41d93cb2 Input hash. Input changes expire cache keys.
  37. JSON APIs w/AMS • AMS = ActiveModel::Serializers • HTTP caching

    everywhere • Auto expiring cache keys • Cache individual JSON fragments, responses composed of discrete JSON fragments. • belongs_to :touch => true • Russian doll caching for collections and records with associations
  38. # ActiveModel::Serializers Patch class PostSerializer < ActiveModel::Serializer # cache JSON

    string def to_json(*args) Rails.cache.fetch "/posts/#{object.cache_key}/to_json" do super end end # cache generated hash def serializable_hash(*args) Rails.cache.fetch "/posts/#{object.cache_key}/hash" do super end end end
  39. Date Handling • Write the UTC date into a data-timestamp

    attribute. • Use javascript to parse timestamps into local dates on the frontend
  40. Data Sharing • Eliminate permutations by using one generic copy.

    • Saves space in memcache • Example: the current user’s name is replaced with “You” on pages. The underlying view contains all the names. Use Javascript to replace the current user’s name with “You”.
  41. Wrap-Up • Use HTTP caching everywhere • Use auto expiring

    keys everywhere (belongs_to :touch => true) • Don’t use sweepers, page or action caching • Beware of things that happen outside the HTTP Request • So much to cover, not enough time.
  42. Questions? Read This • http://www.broadcastingadam.com/ 2012/07/advanced_caching_revised/