Tokyo Cache Cow
Tokyo Cache Cow is MemCache protocol speaking cache server. It offers the ability to delete keys based on a substring as well as aggregate functions based on substrings.
Motivation
Cache sweepers in rails do not currently operate with memcache because the memcache server itself does not support key matching removal operations. After reading www.igvita.com/2009/02/13/tokyo-cabinet-beyond-key-value-store/ and seeing the performance characteristics of that database, I thought I’d give it a go. Event Machine does the heavy lifting on the network end. Performance is currently comparable to memcached.
Prerequisites
You’ll need the eventmachine gem installed. As well, you’ll have to install Tokyo Cabinet itself (available at tokyocabinet.sourceforge.net/index.html) and the Tokyo Cabinet Ruby bindings (available at tokyocabinet.sourceforge.net/rubypkg/)
Example (using the rails client under script/console)
Lets write four keys: other_key, test_key, test_key2 and test_key3.
>> Rails.cache.write('other_key', 'other_value')
=> true
>> Rails.cache.write('test_key', 'test_value')
=> true
>> Rails.cache.write('test_key2', 'test_value2')
=> true
>> Rails.cache.write('test_key3', 'test_value3')
=> true
Read back test_key and make sure life is still good.
>> Rails.cache.read('test_key2')
=> "test_value2"
But lets delete test_key2 for fun.
>> Rails.cache.delete('test_key2')
=> true
Confirm that test_key2 is really gone.
>> Rails.cache.read('test_key2')
=> nil
.. but our other keys (namely, test_key) are just fine, thank you.
>> Rails.cache.read('test_key')
=> "test_value"
.. lets nuke EVERYTHING with test_key in it though.
>> Rails.cache.delete_matched('test_key')
=> true
Now test_key and test_key3 are both nuked.
>> Rails.cache.read('test_key')
=> nil
>> Rails.cache.read('test_key3')
=> nil
But other_key is still peachy.
>> Rails.cache.read('other_key')
=> "other_value"
Aggregate functions
You can invoke aggregates by using the functions avg()
, count()
, sum()
, min()
and max()
.
>> Rails.cache.write('hello1', '1')
=> true
>> Rails.cache.write('hello2', '2')
=> true
>> Rails.cache.write('hello3', '3')
=> true
>> Rails.cache.read('avg(hello)')
=> "2.0"
>> Rails.cache.read('sum(hello)')
=> "6.0"
Usage
Server
>> tokyo_cache_cow --help
Usage: tokyo_cache_cow [options]
Options:
-p, --port[OPTIONAL] Port (default: 11211)
-a, --address[OPTIONAL] Address (default: 0.0.0.0)
-c, --class[OPTIONAL] Cache provider class (default: TokyoCacheCow::Cache::TokyoCabinetMemcache)
-r, --require[OPTIONAL] require
-f, --file[OPTIONAL] File (default: /tmp/tcc-cache)
-d, --daemonize[OPTIONAL] Daemonize (default: false)
-P, --pid[OPTIONAL] Pid file (default: /tmp/tcc.pid)
-m, --matcher[OPTIONAL] Special flag for doing matched deletes (not enabled by default)
-h, --help Show this help message.
Client
Any standard memcache client will do, however, there is a special initialize for Rails to enable delete_matched functionality within the built-in memcache client there. To install this into rails:
script/plugin install git://github.com/joshbuddy/tokyo-cache-cow.git
Caveats
Right now there is no compression on disk. Things could get big, but Tokyo Cabinet does support various compression schemes, so exposing that to the runner should be trivial. Potentially performance could degrade after time, but I have yet to seriously investigate if thats the case.
Feel the moo.