Cache Money Talk: Practical Application
-
Upload
wolfram-arnold -
Category
Technology
-
view
4.681 -
download
0
description
Transcript of Cache Money Talk: Practical Application
Wolfram ArnoldWolfram Arnoldwww.rubyfocus.bizwww.rubyfocus.biz
Cache Money
Practical Application
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?
:cache => Rails
Page CachingPage Caching
class CorpController < ApplicationControllerclass CorpController < ApplicationController
caches_pages :about, :privacy, :toscaches_pages :about, :privacy, :tos
endend
● Bypasses application● Fast!● Static content only—rare● Requires local file system
:choose => Store
BackendsBackends
config.cache_store =config.cache_store =
:mem_cache_store:mem_cache_store
:memory_store:memory_store
:file_store, "/path/to/cache/directory":file_store, "/path/to/cache/directory"
:drb_store, "druby://localhost:9192":drb_store, "druby://localhost:9192"
Action CachingAction Caching
class UsersController < ApplicationControllerclass UsersController < ApplicationController
caches_action :showcaches_action :show
def updatedef update
expires_actionexpires_action
endend
endend
● Can run before filters● ~1 order of magnitude slower
Fragment CachingFragment Caching
<% cache do %><% cache do %>
Some complicated slow-to-renderSome complicated slow-to-render
content here...content here...
<% end %><% end %>
● Stores of rendered snippets keyed on action● Maintaining expiration code tricky
:sweep => Cache
SweepingSweeping
class ListSweeper < ActionController::Caching::Sweeperclass ListSweeper < ActionController::Caching::Sweeper
observe Listobserve List
def def after_saveafter_save(record)(record)
expire_page(:controller => "lists",expire_page(:controller => "lists",
:action => %w( show public feed ), :id => list.id):action => %w( show public feed ), :id => list.id)
expire_action(:controller => "lists", :action => "all")expire_action(:controller => "lists", :action => "all")
# every place where List instances are used# every place where List instances are used
endend
class ListsController < ApplicationControllerclass ListsController < ApplicationController
caches_action :index, :show, :public, :feedcaches_action :index, :show, :public, :feed
cache_sweeper :list_sweeper, :only => [ :edit, :destroy ]cache_sweeper :list_sweeper, :only => [ :edit, :destroy ]
endend
:cache => Query
Query CacheQuery Cache
User Load (0.1ms)User Load (0.1ms) SELECT * FROM `users` SELECT * FROM `users` WHERE (`users`.`id` = 1572302630)WHERE (`users`.`id` = 1572302630)
CACHE (0.0ms)CACHE (0.0ms) SELECT * FROM `users` SELECT * FROM `users` WHERE (`users`.`id` = 1572302630) WHERE (`users`.`id` = 1572302630)
● Exact same query● No updates in between
:cache => Models
:cache => MONEY
User.find(id)User.find(id)
Cache Money
User.find(id)User.find(id)
alias_method_chain :find_every, :cachealias_method_chain :find_every, :cache
alias_method_chain :find_from_ids, :cachealias_method_chain :find_from_ids, :cache
alias_method_chain :calculate, :cachealias_method_chain :calculate, :cache
User.get(“User:1/#{id}”)User.get(“User:1/#{id}”)
Indexed
class User < ActiveRecord::Baseclass User < ActiveRecord::Base
index :nameindex :name, :order => :desc, :order => :desc
endend
User.all(:conditions => ['id = ?', ...])User.all(:conditions => ['id = ?', ...])
User.find_by_User.find_by_namename(:order => “DESC”)(:order => “DESC”)
User.all(:conditions => ['User.all(:conditions => ['namename = ?, ...], = ?, ...], :order => “DESC”):order => “DESC”)
User.countUser.count
User.count(User.count(:name:name => “Jones”) => “Jones”)
:expiry => how?
Expiry
after_create :add_to_cachesafter_create :add_to_caches
after_update :update_cachesafter_update :update_caches
after_destroy :remove_from_cachesafter_destroy :remove_from_caches
User.createUser.create
@user.attributes = { :name => ... }@user.attributes = { :name => ... }
:what => else?
Associations
class Userclass User
has_many :videoshas_many :videos
endend
class Videoclass Video
index [id, user_id]index [id, user_id]
belongs_to :userbelongs_to :user
endend
@user.videos.find(id)@user.videos.find(id)
:must_have => Joins
Joins :throughclass Videoclass Video
has_many :taggings has_many :taggings
has_many :tags, :through => :taggingshas_many :tags, :through => :taggings
endend
SELECT `tags`.* FROM `tags`SELECT `tags`.* FROM `tags`INNER JOININNER JOIN taggings taggings ONON tags.id = taggings.tag_id tags.id = taggings.tag_idWHERE ((`taggings`.taggable_id = 1912438135) AND WHERE ((`taggings`.taggable_id = 1912438135) AND (`taggings`.taggable_type = 'Video'))(`taggings`.taggable_type = 'Video'))
:include => Trouble
Joins :includeVideo.find(:all, Video.find(:all, :include:include => :taggings, => :taggings,
:conditions => “taggings.id IS NOT NULL”):conditions => “taggings.id IS NOT NULL”)
SELECT `videos`.`id` AS t0_r0, `videos`.`user_id` AS SELECT `videos`.`id` AS t0_r0, `videos`.`user_id` AS t0_r1, `taggings`.`id` AS t1_r0, t0_r1, `taggings`.`id` AS t1_r0, `taggings`.`tag_id` AS t1_r1, `taggings`.`tag_id` AS t1_r1, `taggings`.`taggable_id` AS t1_r2, `taggings`.`taggable_id` AS t1_r2, `taggings`.`taggable_type` AS t1_r3 FROM `videos``taggings`.`taggable_type` AS t1_r3 FROM `videos`LEFT OUTER JOINLEFT OUTER JOIN `taggings` `taggings` ONON `taggings`.taggable_id = `videos`.id AND `taggings`.taggable_id = `videos`.id AND `taggings`.taggable_type = 'Video' WHERE `taggings`.taggable_type = 'Video' WHERE (taggings.id IS NOT NULL)(taggings.id IS NOT NULL)
class Videoclass Video
has_many :taggingshas_many :taggings
endend
Video.find(:all, :include => :taggings)Video.find(:all, :include => :taggings)
→→ 1 SQL Query, not cacheable1 SQL Query, not cacheable
Video.find(:all).each do |v|Video.find(:all).each do |v|
v.taggingsv.taggings
endend
→ → N + 1 SQL Queries, cacheableN + 1 SQL Queries, cacheable
:options_for => Joins
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
:caching => Evolution
If it's supposed to help if must taste bitter.
Even if tastes bitter, it might still not help.
Thank You!
github.com/nkallen/cache-money/
github.com/wolframarnold/acts_as_most_popular
Wolfram ArnoldWolfram Arnoldwww.rubyfocus.bizwww.rubyfocus.biz