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
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.
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
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.
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!