Rails MemcachedStore for the Memcached Gem
I’ve been experimenting with the memcached gem (the c gem that runs up to 150x faster than memcache-client). Merb already has pretty good support for the Memcached gem and in typical merb style does it better with fewer lines of code but I digress. Here’s my test implementation, so far so good nothing really heavy on it yet. YMMV so be warned this is not tested in a production environment don’t say I didn’t warn you so and such.
# in your environment file
config.cache_store = :memcached_store, 'localhost:11211'
# lib/memcached_store.rb
require 'memcached'
module ActiveSupport
module Cache
class MemcachedStore < Store
attr_reader :addresses
def initialize(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost"] if addresses.empty?
@addresses = addresses
@data = Memcached::Rails.new(addresses, options)
end
def read(key, options = nil)
super
@data.get(key, raw?(options))
rescue Memcached::Error => e
logger.error("Memcached::Error (#{e}): #{e.message}")
nil
end
# Set key = value. Pass :unless_exist => true if you don't
# want to update the cache if the key is already set.
def write(key, value, options = nil)
super
method = options && options[:unless_exist] ? :add : :set
response = @data.send(method, key, value, expires_in(options), raw?(options))
response == nil
rescue Memcached::Error => e
logger.error("Memcached::Error (#{e}): #{e.message}")
false
end
def delete(key, options = nil)
super
response = @data.delete(key, expires_in(options))
response == nil
rescue Memcached::Error => e
logger.error("Memcached::Error (#{e}): #{e.message}")
false
end
def exist?(key, options = nil)
# Doesn't call super, cause exist? in memcache is in fact a read
# But who cares? Reading is very fast anyway
!read(key, options).nil?
end
def increment(key, amount = 1)
log("incrementing", key, amount)
@data.incr(key, amount)
rescue Memcached::Error
nil
end
def decrement(key, amount = 1)
log("decrement", key, amount)
@data.decr(key, amount)
rescue Memcached::Error
nil
end
def delete_matched(matcher, options = nil)
super
raise "Not supported by Memcache"
end
def clear
@data.flush_all
end
def stats
@data.stats
end
private
def expires_in(options)
(options && options[:expires_in]) || 0
end
def raw?(options)
options && options[:raw]
end
end
end
end
Snippet
Thoughts on User.current and Thread Safety

Pratik Naik has an interesting post Thread safety for your Rails about what thread safety in Rails means (and what it doesn’t). One of the interesting points is that a common pattern of using class attributes is not thread safe and can lead to race conditions. I found it interesting because I’m a big proponent of User.current. I’ve always gotten great millage out of it and I’ve always felt like it’s a necessary evil for protecting user_id attributes from mass assignment and not totally driving yourself nuts with @foo.user_id = session[:user_id] all over your controller actions.
So the problem now is that if you ever intend to use User.current with a thread safe Rails app you’ve got to adjust fire. My thoughts are now turned toward investigating the following.
class ApplicationController < ActionController::Base
before_filter :current_user_id
# Set the value
def current_user_id
Thread.current[:current_user_id] = session[:user_id]
end
end
class User < ActiveRecord::Base
def self.current
Thread.current[:current_user] ||= self.fetch_by_id(Thread.current[:current_user_id])
end
end
Snippet
This seems like a thread safe method of accessing the current User without trading off the real functionality we want.

