Rails MemcachedStore for the Memcached Gem 1
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 9

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.

