Lukasz Wrobel on Ruby cache systems

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

This is a very good write up by Lukasz Wrobel, about different cache techniques.

Instead of mixing cache logic into your methods, try to wrap method calls with cache handler. This shouldn’t be difficult, especially in view of some nice Ruby’s features.

Let’s wrap method call with a block like this:

posts = cached(‘posts’) do
get_posts()
end

It looks much more cleaner than the previous example, but when does the caching and retrieving data from cache takes place? Let’s try to implement the cached() method now:

def cached(cache_key)
if result = memcached.get(cache_key)
return result
else
result = yield
memcached.set(cache_key, result)

return result
end
end

Laziness is a virtue, remember? The block of code associated with the cached() method call is being run only when necessary, i.e. when cache data for specific key is empty. It means that the underlying code is called only once per amount of time (providing there was no cache eviction). Of course, this simple cached() method implementation can be further extended to handle method parameters and support various types of storage incl. filesystem and local memory. Wrapping expensive code is easy in Ruby as well as in any other programming language supporting anonymous functions.

By the way, storing results of function calls is often referred to as memoization, i.e. when calculation results are stored for future reference. This is similar to dynamic programming, which allows to quickly solve some computationally complex problems.

There are some Ruby gems available which perform memoization, yet they only store results in local memory. This is what we should avoid in a distributed, web environment – a situation when each machine holds its own, possibly stale copy of data and the same call is being made on many machines over and over again, is cleary inacceptable. However, these gems can be handy to optimize Ruby programs running on a local machine.

Memoization wrapped with blocks is at least a little bit cleaner than the “quick and dirty” approach and it’s not strictly bound to implementation details. However, if you wish to invalidate cache data on demand and not rely on TTL only, you should take into account that this technique suffers from spreading invalidation logic to the same extent as the previous one.

Post external references

  1. 1
    http://lukaszwrobel.pl/blog/memcached-and-ruby
Source