Cache Money Talk: Practical Application

3,046 views

Published on

Cache Money is a new model caching framework for Active Record that transparently stores, synchronizes and expires model instances in memcached, through Active Record callbacks. It's a framework that provides almost all the pieces of transparently turning an application without model caching into one with model caching at the flip of a switch. In this presentation I'll show what the framework delivers, its limitation and what problems to watch out for when putting it in as drop-in intermediary for your database connection.

Published in: Technology, Business
1 Comment
5 Likes
Statistics
Notes
No Downloads
Views
Total views
3,046
On SlideShare
0
From Embeds
0
Number of Embeds
28
Actions
Shares
0
Downloads
36
Comments
1
Likes
5
Embeds 0
No embeds

No notes for slide

Cache Money Talk: Practical Application

  1. 1. Cache Money Practical Application Wolfram Arnold www.rubyfocus.biz
  2. 2. Outline Caching in Rails ● Model caching ● Cache Money API ● What's not covered? ● How do I know it's cached? ● Advice—best bang for the buck? ● wolfram@rubyfocus.biz
  3. 3. :cache => Rails wolfram@rubyfocus.biz
  4. 4. Page Caching class CorpController < ApplicationController caches_pages :about, :privacy, :tos end Bypasses application ● Fast! ● Static content only—rare ● Requires local file system ● wolfram@rubyfocus.biz
  5. 5. :choose => Store wolfram@rubyfocus.biz
  6. 6. Backends config.cache_store = :mem_cache_store :memory_store :file_store, quot;/path/to/cache/directoryquot; :drb_store, quot;druby://localhost:9192quot; wolfram@rubyfocus.biz
  7. 7. Action Caching class UsersController < ApplicationController caches_action :show def update expires_action end end Can run before filters ● ~1 order of magnitude slower ● wolfram@rubyfocus.biz
  8. 8. Fragment Caching <% cache do %> Some complicated slow-to-render content here... <% end %> Stores of rendered snippets keyed on action ● Maintaining expiration code tricky ● wolfram@rubyfocus.biz
  9. 9. :sweep => Cache wolfram@rubyfocus.biz
  10. 10. Sweeping class ListSweeper < ActionController::Caching::Sweeper observe List def after_save(record) expire_page(:controller => quot;listsquot;, :action => %w( show public feed ), :id => list.id) expire_action(:controller => quot;listsquot;, :action => quot;allquot;) # every place where List instances are used end class ListsController < ApplicationController caches_action :index, :show, :public, :feed cache_sweeper :list_sweeper, :only => [ :edit, :destroy ] wolfram@rubyfocus.biz end
  11. 11. :cache => Query wolfram@rubyfocus.biz
  12. 12. Query Cache User Load (0.1ms) SELECT * FROM `users` WHERE (`users`.`id` = 1572302630) CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`id` = 1572302630) Exact same query ● No updates in between ● wolfram@rubyfocus.biz
  13. 13. :cache => Models wolfram@rubyfocus.biz
  14. 14. Models Controllers Views memcached DB wolfram@rubyfocus.biz
  15. 15. :cache => MONEY wolfram@rubyfocus.biz
  16. 16. User.find(id) wolfram@rubyfocus.biz
  17. 17. Models memcached DB wolfram@rubyfocus.biz
  18. 18. Cache Money User.find(id) alias_method_chain :find_every, :cache alias_method_chain :find_from_ids, :cache alias_method_chain :calculate, :cache User.get(“User:1/#{id}”) wolfram@rubyfocus.biz
  19. 19. Indexed class User < ActiveRecord::Base index :name, :order => :desc end User.all(:conditions => ['id = ?', ...]) User.find_by_name(:order => “DESC”) User.all(:conditions => ['name = ?, ...], :order => “DESC”) User.count wolfram@rubyfocus.biz User.count(:name => “Jones”)
  20. 20. :expiry => how? wolfram@rubyfocus.biz
  21. 21. Expiry after_create :add_to_caches after_update :update_caches after_destroy :remove_from_caches User.create @user.attributes = { :name => ... } @user.destroy wolfram@rubyfocus.biz
  22. 22. :what => else? wolfram@rubyfocus.biz
  23. 23. Associations class User class Video has_many :videos index [id, user_id] end belongs_to :user end @user.videos.find(id) wolfram@rubyfocus.biz
  24. 24. :must_have => Joins wolfram@rubyfocus.biz
  25. 25. Joins :through class Video has_many :taggings has_many :tags, :through => :taggings end @video.tags SELECT `tags`.* FROM `tags` INNER JOIN taggings ON tags.id = taggings.tag_id WHERE ((`taggings`.taggable_id = 1912438135) AND (`taggings`.taggable_type = 'Video')) wolfram@rubyfocus.biz
  26. 26. :include => Trouble wolfram@rubyfocus.biz
  27. 27. Joins :include Video.find(:all, :include => :taggings, :conditions => “taggings.id IS NOT NULL”) SELECT `videos`.`id` AS t0_r0, `videos`.`user_id` AS t0_r1, `taggings`.`id` AS t1_r0, `taggings`.`tag_id` AS t1_r1, `taggings`.`taggable_id` AS t1_r2, `taggings`.`taggable_type` AS t1_r3 FROM `videos` LEFT OUTER JOIN `taggings` ON `taggings`.taggable_id = `videos`.id AND `taggings`.taggable_type = 'Video' WHERE (taggings.id IS NOT NULL) wolfram@rubyfocus.biz
  28. 28. class Video has_many :taggings end Video.find(:all, :include => :taggings) → 1 SQL Query, not cacheable Video.find(:all).each do |v| v.taggings end → N + 1 SQL Queries, cacheable wolfram@rubyfocus.biz
  29. 29. :options_for => Joins wolfram@rubyfocus.biz
  30. 30. Options for join queries Denormalize ● Use after_save, after_destroy to keep in sync – Maintain your own index ● e.g. acts_as_most_popular – Deconstruct into multiple queries ● e.g. drop :include – wolfram@rubyfocus.biz
  31. 31. :caching => Evolution wolfram@rubyfocus.biz
  32. 32. If it's supposed to help if must taste bitter. wolfram@rubyfocus.biz
  33. 33. Even if tastes bitter, it might still not help. wolfram@rubyfocus.biz
  34. 34. Thank You! github.com/nkallen/cache-money/ github.com/wolframarnold/acts_as_most_popular Wolfram Arnold www.rubyfocus.biz wolfram@rubyfocus.biz

×