memcache API happiness for C and Ruby users...

Sean Chittenden sean at
Wed Jan 5 11:42:21 PST 2005

> The only bit that I have outstanding is with multi-get requests in 
> that I need to do add a scatter-gather algo to have the get's for the 
> various keys distributed across the appropriate servers then have the 
> various keys reassembled and returned in the correct order.  I'll 
> likely tackle that in a 1.3.0 release, however and not in the 1.2.0 
> cycle.  Please test and let me know if anyone has any probs w/ this 
> version of memcache(3).

A little bit more on this.  Here's an example symptom (using the Ruby 
API, however the problem is with memcache(3) and not the Ruby API.  I'd 
wager that other APIs have this problem too).

> m =
> m.add_server('')
> m.add_server('')
> m.add_server('')
> m['key1'] = 'val1'
> m['key2'] = 'val2'
> m['key3'] = 'val4'
> m['key4'] = {'foo' => 'val5', 'bar' => 'val6'}
> p m['key1']
> p m['key2']
> p m['key3']
> p m['key4']
> p m.get_a('key1', 'key2', 'key3', 'key4')
> p m.get_h('key1', 'key2', 'key3', 'key4')

The above has the following output:

> "val1"
> "val2"
> "val4"
> {"foo"=>"val5", "bar"=>"val6"}
> ["val1", nil, "val4", nil]
> {"key1"=>"val1", "key2"=>nil, "key3"=>"val4", "key4"=>nil}

If you comment out all but one of the above #add_server() lines, you 
get the correct output, however:

> "val1"
> "val2"
> "val4"
> {"foo"=>"val5", "bar"=>"val6"}
> ["val1", "val2", "val4", {"foo"=>"val5", "bar"=>"val6"}]
> {"key1"=>"val1", "key2"=>"val2", "key3"=>"val4", 
> "key4"=>{"foo"=>"val5", "bar"=>"val6"}}

The problem being that the multi-get's send all of the keys to the 
server calculated by 'key1' and it doesn't correctly send get requests 
to each of the servers, collate the results, then return the result set 
back to the client.  This was what I meant by needing to add 
scatter/gather support to memcache(3).

If anyone has any neat tricks for doing this in parallel, drop me a 
line offline.  I refuse to do this in sequence/serially so any ideas 
are going to require either async IO, or non-blocking IO.  Ideally I'd 
use use pthreads, but that's less than portable (despite the p in 
pthreads).  Who knows, maybe I'll start abstracting IO handling and can 
sneak in the use of kqueue(2) or libevent(3).  Efficient reassembly 
that's not O(N^2) is my main concern, however.  Anything that's more 
expensive than O(2N) isn't gunna cut it.  Anyway, like I said, I'll 
probably get to the scatter/gather portion of memcache(3) soonish.  In 
the meantime, enjoy the ruby bindings.  I'll do some benchmarking later 
to justify to myself the need to add Memcache::Request and 
Memcache::Response class (hint: need to prevent excessive object 
creation in tight loops).  -sc

Sean Chittenden

More information about the memcached mailing list