Get your Caffeine on
I recently stumbled upon Adocca plugins, which is a suite of plugins / extensions extracted from the development of trig.com.
This includes a nifty Ruby extension, Caffeine, built on libmemcache, the C API for Memcached.The API is almost 100% compatible with memcache-client, with the following exceptions :
- No get_multi ( may or may not be a big deal for you )
- Multithreaded by default, only accepts a single :namespace as options Hash
- MemCache#servers is write only and you can't overwrite previously defined servers
- MemCache#get accepts either a String or Array, the latter which forces a namespace lookup (more on this later)
I'll continue with a quick rundown ...
- Example installation ( OS X )
- Requires a properly configured Memcached environment
Libmemcache installation
Grab it here, extract and ....
libmemcache-1.4.0.rc2 lourens$ ./configure && make && make install
Libuuid installation
Download the e2fsprogs package, extract and ...
e2fsprogs-1.39 lourens$ ./configure
e2fsprogs-1.39 lourens$ cd lib/uuid
e2fsprogs-1.39 lourens$ make && sudo make install
We don't need the whole package, just libuuid.
Caffeine installation
lourens$ sudo gem install caffeine
Building native extensions. This could take a while...
caffeine.c: In function 'key_from_segments':
caffeine.c:712: warning: incompatible implicit declaration of built-in function 'mempcpy'
caffeine.c: In function 'md5_digest':
caffeine.c:872: warning: pointer targets in passing argument 1 of 'MD5' differ in signedness
/usr/bin/ld: warning multiple definitions of symbol _setregid
/opt/local/lib/libruby.dylib(process.o) definition of _setregid
/usr/lib/gcc/i686-apple-darwin8/4.0.1/../../../libpthread.dylib(setregid.So) definition of _setregid
/usr/bin/ld: warning multiple definitions of symbol _setreuid
/opt/local/lib/libruby.dylib(process.o) definition of _setreuid
/usr/lib/gcc/i686-apple-darwin8/4.0.1/../../../libpthread.dylib(setreuid.So) definition of _setreuid
ruby extconf.rb install caffeine
checking for mc_new() in -lmemcache... yes
checking for uuid_generate() in -luuid... yes
checking for main() in -lssl... yes
creating Makefile
make
gcc -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin8.9.1 -I. -O -pipe -I/opt/local/include -fno-common -ggdb -O0 -Df -c caffeine.c
cc -dynamic -bundle -undefined suppress -flat_namespace -L/opt/local/lib -L"/opt/local/lib" -o caffeine.bundle caffeine.o -lruby -lssl -luuid -lmemcache -lpthread -ldl -lobjc -lspread
make install
/usr/bin/install -c -m 0755 caffeine.bundle /opt/local/lib/ruby/gems/1.8/gems/caffeine-0.0.2/lib
make clean
Successfully installed caffeine-0.0.2
Namespaces
The interesting aspect about this client lib is the elegant namespace support, not to be confused with your connection namespace.One could have keys like the following (pseudo code) :
- bookings-development:Account:1:Addresses:2
- bookings-development:Account:20:Addresses:12
Very simple to sweep a whole set of related keys eg. to expire Account information, we just unlink the Account namespace.Actual keys resemble this ...
- bookings-development:7f722ed3a969554d010e62510217cc
... courtesy of libuuid to avoid namespace / key trashing.
Performance?
I didn't gain all that much by using Caffeine in my dev / staging environment, but mileage varies, I like to keep good karma and thus won't open a 'scaling / performance' can of worms.
Integration
The following svn diff applied to cache_fu revision 240 should have you up and running without hickups.
lourens$ svn diff vendor/plugins/cache_fu
Index: vendor/plugins/cache_fu/lib/acts_as_cached/config.rb
===================================================================
--- vendor/plugins/cache_fu/lib/acts_as_cached/config.rb (revision 2337)
+++ vendor/plugins/cache_fu/lib/acts_as_cached/config.rb (working copy)
@@ -42,9 +42,16 @@
def setup_memcache(config)
config[:namespace] << "-#{RAILS_ENV}"
-
+
+ begin
+ require 'caffeine'
+ Caffeine::MemCache.class_eval{ attr_reader :namespace }
+ rescue
+ end
+
silence_warnings do
- Object.const_set :CACHE, MemCache.new(config)
+ Object.const_set :CACHE, Object.const_defined?( :Caffeine ) ? Caffeine::MemCache.new( { :namespace => config[:namespace] } ) : MemCache.new(config)
+
end
CACHE.servers = Array(config.delete(:servers))
Index: vendor/plugins/cache_fu/lib/acts_as_cached/cache_methods.rb
===================================================================
--- vendor/plugins/cache_fu/lib/acts_as_cached/cache_methods.rb (revision 2337)
+++ vendor/plugins/cache_fu/lib/acts_as_cached/cache_methods.rb (working copy)
@@ -26,7 +26,7 @@
id = args.first
end
- if (item = fetch_cache(id)).nil?
+ if (item = fetch_cache(id)).nil? || item == :MemCache_no_such_entry
set_cache(id, block_given? ? yield : fetch_cachable_data(id), options[:ttl])
else
item
@@ -239,7 +239,7 @@
set_cache
end
- # Lourens Naud?
+ # Lourens Naudé
def expire_cache_with_associations(*associations_to_sweep)
((cache_options[:include] || []) + associations_to_sweep).flatten.uniq.compact.each do |assoc|
Array(send(assoc)).compact.each { |item| item.expire_cache if item.respond_to?(:expire_cache) }
2 comments
Jump to comment form | comments rss [?]