July 15, 2010
Older: Mongo Scout Plugins
Newer: Creating Duplicable Objects
Caching With Mongo
For those of you that do not follow me on Twitter or Github, a while back I released Bin, an ActiveSupport MongoDB cache store. Since I have been quiet here, I thought I would talk a bit about it to help get back in the swing of things.
Using Bin is just like using any other AS cache store.
connection = Mongo::Connection.new
db = connection['bin_cache']
Rails::Initializer.run do |config|
config.cache_store = Bin::Store.new(db)
end
Once you have set things up, you can use all the typical Rails.cache methods.
Rails.cache.write('foo', 'bar')
Rails.cache.read('foo') # 'bar'
Rails.cache.fetch('foo') do
# some expensive thing
end
The list goes on, but in the interest of brevity, I will just link you to the specs. The cool thing about bin is that it supports both ActiveSupport 2 and 3 along with Ruby 1.8.7 and 1.9.1. Oh, and it supports expires with the same API as the memcache store.
That pretty much covers the basics, feel free to go kick the tires or hang around here a bit to learn how I made Bin work with AS2 and AS3.
Supporting AS2/3
In both ActiveSupport 2 and 3, you inhert from ActiveSupport::Cache::Store to create a new store. The difference between the version is quite subtle though. In AS3, you override the methods read, write, etc. as needed and use super with a block to get the inherited functionality. In AS2, you do the same thing, but super does not accept a block. Being that as a community we are now mugwumps (mug on Rails 2 and wumps on Rails 3), I thought it would be nice to support both.
In order to make this happen, I knew all I need to do was shim compatibility for Rails 2. So what I did is create a compatibility class that inherits from ActiveSupport::Cache::Store for AS3 and then if active support’s version is less than 3, I reopen the class and add in the compatibility stuff to make it work like 3. Here is the code in its entirety:
# encoding: UTF-8
module Bin
class Compatibility < ActiveSupport::Cache::Store
def increment(key, amount=1)
yield
end
def decrement(key, amount=1)
yield
end
end
if ActiveSupport::VERSION::STRING < '3'
class Compatibility
def write(key, value, options=nil, &block)
super(key, value, options)
yield
end
def read(key, options=nil, &block)
super
yield
end
def delete(key, options=nil, &block)
super
yield
end
def delete_matched(matcher, options=nil, &block)
super
yield
end
def exist?(key, options=nil, &block)
super
yield
end
end
end
end
So then Bin::Store just inherits from Compatibility:
module Bin
class Store < Compatibility
# ... stuff
end
end
I cringe using a specific version string comparison like above, but it was simple and worked so I went with it. The last piece of the puzzle was setting up rake tasks to run the specs against different active support versions.
namespace :spec do
Spec::Rake::SpecTask.new(:all) do |t|
t.ruby_opts << '-rubygems'
t.verbose = true
end
task :as2 do
sh 'ACTIVE_SUPPORT_VERSION="<= 2.3.8" rake spec:all'
end
task :as3 do
sh 'ACTIVE_SUPPORT_VERSION=">= 3.0.0.beta3" rake spec:all'
end
end
desc 'Runs all specs against Active Support 2 and 3'
task :spec do
Rake::Task['spec:as2'].invoke
Rake::Task['spec:as3'].invoke
end
Note that I make an all task to run the specs then two distinct tasks to run against AS2 and AS3. All those tasks do is set an environment variable that I use in the test to force a particular version.
gem 'activesupport', ENV['ACTIVE_SUPPORT_VERSION']
Now when I run rake, it runs the tests against a 2.3 and a 3.0+ version of ActiveSupport so I know when something goes wrong with either. No flipping gem sets or other shenanigans. As always, if you have improvements or other way so doing stuff like this, please let me know. I am here to learn people.
Using Bin on the last project I worked on to cache large fragments of the layout significantly reduced response times. Always fun to see numbers like that drop after a deploy!
5 Comments
Jul 15, 2010
Do you have any statistics to show the performance of using Mongo in comparison to something like Memcached? Are there any particular benefits?
Jul 15, 2010
Does rails 3 now work with 1.9.1? I thought you could only use 1.8.7 or rails-head (or rc2 etc)
Jul 15, 2010
@Ryan Townsend: Nope, no statistics. If you are looking for pure perf, something that is only in memory is ideal and as such memcached would be. If you already have Mongo running and just need some help speeding up a few things, this will get you there.
@Mark: No clue. I don’t actually follow that. All I know is the tests all pass. :)
Jul 15, 2010
Your compatibility layer is not enough to make it work on Rails 3, it does raise a “NotImplementedError” when trying to call read_entry. write_entry, read_entry and delete_entry should be used instead of write, read and delete a bit like what I’ve done for mongo_store http://github.com/openhood/mongo_store/commit/44b17f3896345bfe5f4c9a2eef2f85d1d329f2d5
Jul 15, 2010
Just try this to see the issue:
then I18n.t(“anything”)
Thoughts? Do Tell...