Fail Silently with Memcache Client
For web applications, [caching is king](http://www.ukgeocachers.co.uk/catalog/cache-king-44mm-button-badge-p-391.html). I've recently been using [memcached](http://danga.com/memcached/) to cache expensive query results in a Rails application, with Seattle RB's [memcache-client](http://seattlerb.rubyforge.org/memcache-client/) as the client library.
The library is solid, but it has one opinion I disagree with: when a memcached instance fails, it throws an exception that your code has to handle. I think that's the wrong default. When a cache fails, *it doesn't matter*. Either the application continues running uncached -- slower, but functional -- or other memcached instances pick up the slack. Neither scenario should require special handling in application code.
Ruby, being awesome, lets me change the library's behaviour easily. Monkey patching may be frowned upon, but it has its uses:
```ruby
# A simple monkey-patch of MemCache so that broken memcached instances don't
# cause fatal errors in the application. Performance may be severely degraded
# but it should be possible to use the app anyway!
#
# A typical use would look something like:
#
# result = if cache.alive?
# fetch = cache.get(:foo)
# if !fetch
# fetch = calculate(:foo)
# cache.set(:foo, fetch)
# end
# fetch
# else
# calculate(:foo)
# end
#
class MemCache
# Does the cache configuration contain any memcached instances that can
# currently be used?
#
# Author: Conor Curran [http://forwind.net/]
#
def alive?
!!cache.servers.detect{ |s| s.alive? }
end
# Rescue from MemCache::MemCacheError -- we want the cache to fail silently
# (at least from the point of view of the application - you should still
# monitor memcached).
#
def get_with_rescue(*args)
get_without_rescue(*args)
rescue MemCache::MemCacheError
end
alias_method :get_without_rescue, :get
alias_method :get, :get_with_rescue
alias_method :[], :get
# Rescue from MemCache::MemCacheError -- we want the cache to fail silently
# (at least from the point of view of the application - you should still
# monitor memcached).
#
def set_with_rescue(*args)
set_without_rescue(*args)
rescue MemCache::MemCacheError
end
alias_method :set_without_rescue, :set
alias_method :set, :set_with_rescue
alias_method :[]=, :set
alias_method :add, :set
# Rescue from MemCache::MemCacheError -- we want the cache to fail silently
# (at least from the point of view of the application - you should still
# monitor memcached).
#
def delete_with_rescue(*args)
delete_without_rescue(*args)
rescue MemCache::MemCacheError
end
alias_method :delete_without_rescue, :delete
alias_method :delete, :delete_with_rescue
end
```
The pattern is straightforward: wrap each method (`get`, `set`, `delete`) with a version that rescues `MemCacheError` and silently returns `nil`. The `alias_method` chain preserves the original implementation so you can still call it directly if needed.
A word of caution: "fail silently" doesn't mean "ignore failures entirely." You should absolutely still be monitoring your memcached instances. This patch just prevents a cache hiccup from becoming an application outage.
These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.