memcached Basics for Rails: Part II 13 comments

posted Monday, January 22, 2007 by topfunky

In Part I I showed how easy it is to install memcached and use it for simple queries or manually stored objects.

Today I was experimenting with running against multiple memcached servers. I was preparing myself for a gruelling process of learning the memcache server configuration format, headaches while trying to get the two servers to talk to each other, etc. Then I read this:

...the API hashes your key to a unique server. If a host goes down, the API re-maps that dead host’s requests onto the servers that are available.

So the servers run without any knowledge of each other. All you have to do is start up a few instances of memcached, then tell your Rails app about them. The Ruby API is responsible for finding out about the servers and distributing the keys between them. It even recovers if one dies.

So easy!

Configuration

I tried this out using a laptop and a desktop machine. The only quirk was that I initially had a firewall on my desktop machine so the memcached port wasn’t open.

# In production.rb
require 'cached_model'

memcache_options = {
  :c_threshold => 10_000,
  :compression => true,
  :debug => false,
  :namespace => 'rails_production',
  :readonly => false,
  :urlencode => false
}

CACHE = MemCache.new memcache_options

# These are the IP addresses and ports of the memcached servers
CACHE.servers = ['192.168.0.3:11211', '192.168.0.2:11211']

Sessions

UPDATE: Argh…my current implementation of this isn’t any faster than ActiveRecord sessions. Back to the drawing board…

UPDATE 2: See the updated plugin for a Memcache session class that uses memcache-client instead of ruby-memcache.

Memcached can sometimes be faster than sessions stored in the database (see RailsExpress). The problem is that you lose all your sessions if you restart the memcached servers.

I have heard legends of an epic session library used at the Robot Coop. Sessions are stored primarily in the database but are also kept in memcached for speedy lookup. I found a nice SqlBypass session template in the Rails source and modified it to work similarly (packaged as a plugin). I’ll be benchmarking it later this week, but it works in the situations I’ve tried it in.

./script/plugin install http://topfunky.net/svn/plugins/db_memcache_store/
# In the Initializer section of environment.rb.
# Explicitly load the library since plugins haven't been loaded at this point.
require "#{RAILS_ROOT}/vendor/plugins/db_memcache_store/lib/db_memcache_store" 
config.action_controller.session_store = CGI::Session::DBMemcacheStore

Startup a few memcached servers.

#           Verbose, Port 11211, 128 MB RAM, IP to listen on
$ memcached -vv      -p 11211    -m 128      -l 192.168.0.2

You’ll see sessions being stored and retrieved. Kill one or more servers and it will keep working.

# Memcached server output
<3 server listening
<6 new client connection
<6 get rails_development:session:bd8b2ba714c059002af6a19283072997
>6 sending key rails_development:session:bd8b2ba714c059002af6a19283072997
>6 END
<6 set rails_development:session:bd8b2ba714c059002af6a19283072997 0 0 216
>6 STORED
<6 get rails_development:session:bd8b2ba714c059002af6a19283072997
>6 sending key rails_development:session:bd8b2ba714c059002af6a19283072997
>6 END

TODO

Actually, it’s back to the drawing board for this one…I found other details about the system used at the Robot Coop and other uses of memcached elsewhere.

13 comments

Leave a response

  • Gravatar icon chris

    Trying to use your memcache session plugin but I get the error: /Users/chris/cart/config/../vendor/plugins/db_memcache_store/lib/db_memcache_store.rb:23: undefined method `cattr_accessor’ for CGI::Session::DBMemcacheStore::DBMemcacheSession:Class (NoMethodError)

    I’m on edge rails.

  • Gravatar icon topfunky

    Yeah, it’s very experimental at this point. Check back in a week or two for an updated, optimized version.

  • Gravatar icon dean

    this is very interesting stuff. my co-worker and I have been looking at caching in Rails in general, and we were just wondering yesterday why someone would use memcache to store sessions, when they aren’t replicated, but just distributed. I like your idea of a DB-backed memcache session-store. (I almost started working on one myself) Once it’s functional, the only question left is performance. I’ll be watching this closely.

  • Gravatar icon Michael

    Couldn’t you just some how use Robot Coop’s cached_model on the Session model itself? I was thinking that this was your ultimate goal, having memcached sessions after they are retrieved from the db so subsequent reads are from memory.

  • Gravatar icon Brian

    Does any of this work with Rails 1.2x? cached_model doesn’t work and I can’t seem to use Cache.put on any relevant data (ie models). I get the following:

    TypeError: can’t dump from (druby://localhost:42531) C:/ruby/lib/ruby/1.8/drb/drb.rb:395:in `_dump’ from (druby://localhost:42531) C:/ruby/lib/ruby/gems/1.8/gems/memcache-client-1.2.1/lib/memcache.rb:225:in `set’ .............

    Just wondering if I need to set my environment up any differently.

  • Gravatar icon topfunky

    There was a hard-coded SQL statement in CachedModel that kept it from caching anything under Rails 1.2.1. I submitted a bug report and fix but it hasn’t been released yet.

    I did use it with Cache.put and Rails 1.2.1. I’m confused by the calls to druby in there…are you using memcached and druby together?

  • @topfunky – I believe I came across the same bug that you did. Just to clarify, the temporary fix is to change line 149 in cached_model.rb so that the LIMIT statement is conditional, right?

    
    return super unless args.first =~ /^SELECT \* FROM #{table_name} WHERE \(#{table_name}\.#{primary_key} = '?(\d+)'?\)( +LIMIT 1)?/
    

    You said you submitted a patch – to Rails or to CachedModel?

  • I have the same problem with chris below:

    Trying to use your memcache session plugin but I get the error: /Users/chris/cart/config/../vendor/plugins/db_memcache_store/lib/db_memcache_store.rb:23: undefined method `cattr_accessor’ for CGI::Session::DBMemcacheStore::DBMemcacheSession:Class (NoMethodError)

    can you help me how to fix the bug? i can not find any update for this plugin.

  • Gravatar icon Roland

    CachedModel seems to be totally broken with rails 1.2.3 and beyond. It does simply nothing anymore. :-(

  • Gravatar icon Roland

    Looks like the hardcoded SQL is still there and not fixed.

    this looks like it works (MySQL, rails 1.2.5):

    def self.find_by_sql(args) return super unless args.first =~ /^SELECT \ FROM #{table_name} WHERE \(#{table_name}\.`#{primary_key}` = ’?(\d+)’?\)/

  • Gravatar icon Ben

    Made some adjustments to the self.find_by_sql regular expression and it worked for me with SQLserver & Rails 1.2.5

    return super unless args.first =~ /^SELECT \* FROM #{table_name} WHERE \(#{table_name}\.\[#{primary_key}\] = ’?(\d+)’?\) $/

  • Gravatar icon topfunky

    Bugs and patches can be filed with the memcache-client project here:

    http://rubyforge.org/tracker/?group_id=1513

  • Gravatar icon Pedro

    the plugin url is broken

Your Comment

Nuby on Rails

Geoffrey Grosenbach / Ruby / Code / Graphics / Design / Rails / Merb / Javascript / CSS

Manufactured with

Subscribe

Subscribe (RSS)