RailsConf 2007: Chris Wanstrath: Kickin' Ass with Cache-fu
Posted by Nick Sieger Sat, 19 May 2007 20:34:00 GMT
Chris is here to talk about games, since he used to work for Gamespot. He coded PHP, which is like training wheels without the bike. He had to sit in a glass cube and help keep the site running during E3 last year. There were 100 gajillion teenage boys during their lunch break hitting refresh, and it all blew up. Couldn’t even gzip the responses, because the servers heated up to much. They served 50M pages in a day, without downtime. They did it with Memcache.
Memcache is a distributed hash -- multiple daemons running on different servers. Developed by Livejournal for their infrastructure, you just put up the servers, and they just work.
Should you use Memcache? No. YAGNI, UYRDNI (unless you really do need it).
Rails and Memcache
Fragments, Actions, Sessions, Objects, cache it all. You can use:
memcache-client
(by Robot-coop guys/Eric Hodel). Marshal.unload is 40 times faster than Object.new/loading from the database.- CachedModel -- integration with ActiveRecord
- Fragment Cache Store
- Memcache session store
...or...
cache_fu
Or, acts_as_cached
. It knows about all the aforementioned objects, with a single YAML config file (config/memcached.yml
). Word to the wise: don’t use names in your server config file. Use IPs, avoid BIND and connections to the servers with every connection. Don’t let DNS outages bring down your servers.
get_cache
expire_cache
This is all you need -- if you’re using set_cache
, you probably don’t understand how the plugin works. Expire cache on the “after save” hook, which allows you to cache ID misses as well.
class Presentation < ActiveRecord::Base
acts_as_cached
after_save :expire_cache
end
Example: only cache published items
class Presentation < ActiveRecord::Base
acts_as_cached :conditions => 'published = 1'
end
Cached-scoped-finders (if somebody thinks of a good name, let Chris know). The idea is to move custom finder logic to a method on your model, and then wrap a cache-scoping thingy around it. cache_fu
ties this up nicely by giving you a cached
method on AR::Base.
class Topic < ActiveRecord::Base
def self.weekly_popular
Topic.find :all, ...
end
end
Topic.cached(:weekly_popular)
Adding date to cache key with alias_method_chain
:
def self.cache_key_with_date(id)
...
end
class << self
alias_method_chain :cache_key, :date
end
Cached loads by ID: Topic.find(1, 2, 3)
moves to Topic.get_cache(1, 2, 3)
, which can parallelize calls to memcached and bring them back as they’re ready.
user_ids = @topic.posts.map(&:user_id).uniq
@users = User.get_cache(user_ids)
You can also cache associations, so that you’re navigating associations via Memcache.
Cache overrides
class ApplicationController < ActionController::Base
before_filter :set_cache_override
def set_cache_override
ActsAsCached.skip_cache_gets = !!params[:skip_cache]
end
end
reset_cache
: Slow, uncached operations can sometimes queue up and wedge a site. Instead, issue cache resets on completion of a request, rather than expiring beforehand. That way, requests that continue to pile up will still use the cached copy until the rebuild is complete.
class Presentation < ActiveRecord::Base
after_save :reset_cache
end
Versioning: a way to expire cache on new code releases
class Presentation < ActiveRecord::Base
acts_as_cached :version => 1
end
Deployment: Chris recommends using Monit to ensure your Memcache servers are up.
libketama
: consistent hashing that gives you the ability to redeploy Memcache servers without invalidating all the keys.
Q: Page caching? A: Nginx with native Memcache page caching, but outside of Rails domains.
Lots of other questions, but dude, Chris talks too fast!
Thanks Nick, I tried to trackback but I may have failed! here is a part I wrote; “Once I found that Nick was blogging, and found that I can catch up with presentations, that I have missed!, I gave up even thinking about writing. He did write a lot of very good material, I spent a lot of time reading his blog already!”
Awesome notes! Great job, man.