remcached
- Ruby EventMachine memCACHED client implementation
- provides a direct interface to the memcached protocol and its semantics
- uses the memcached binary protocol to reduce parsing overhead on the server side (requires memcached >= 1.3)
- supports multiple servers with simple round-robin key hashing (TODO: implement the libketama algorithm) in a fault-tolerant way
- writing your own abstraction layer is recommended
- uses RSpec
- partially documented in RDoc-style
Callbacks
Each request may be passed a callback. These are not two-cased (success & failure) EM deferrables, but standard Ruby callbacks. The rationale behind this is that there are no usual success/failure responses, but you will want to evaluate a response[:status]
yourself to check for cache miss, version conflict, network disconnects etc.
A callback may be kept if it returns :proceed
to catch multi-response commands such as STAT
.
remcached has been built with fault tolerance in mind: a callback will be called with just {:status => Memcached::Errors::DISCONNECTED}
if the network connection has went away. Thus, you can expect your callback will be called, except of course you're using quiet commands. In that case, only a "non-usual response" from the server or a network failure will invoke your block.
Multi commands
The technique is described in the binary protocol spec in section 4.2. Memcached.multi_operation
will help you exactly with that, sending lots of those quiet commands, except for the last, which will be a normal command to trigger an acknowledge for all commands.
This is of course implemented per-server to accomodate load-balancing.
Usage
First, pass your memcached servers to the library:
Memcached.servers = %w(localhost localhost:11212 localhost:11213)
Note that it won't be connected immediately. Use Memcached.usable?
to check. This however complicates your own code and you can check response[:status] == Memcached::Errors::DISCONNECTED
for network errors in all your response callbacks.
Further usage is pretty straight-forward:
Memcached.get(:key => 'Hello') do |response|
case response[:status]
when Memcached::Errors::NO_ERROR
use_cached_value response[:value] # ...
when Memcached::Errors::KEY_NOT_FOUND
refresh_cache! # ...
when Memcached::Errors::DISCONNECTED
proceed_uncached # ...
else
cry_for_help # ...
end
end
end
Memcached.set(:key => 'Hello', :value => 'World',
:expiration => 600) do |response|
case response[:status]
when Memcached::Errors::NO_ERROR
# That's good
when Memcached::Errors::DISCONNECTED
# Maybe stop filling the cache for now?
else
# What could've gone wrong?
end
end
end
Multi-commands may require a bit of precaution:
Memcached.multi_get([{:key => 'foo'},
{:key => 'bar'}]) do |responses|
# responses is now a hash of Key => Response
end
It's not guaranteed that any of these keys will be present in the response. Moreover, they may be present even if they are a usual response because the last request is always non-quiet.
HAPPY CACHING!