Rails are providing developers with really excellent abstractions, but it’s always good to know what’s under the hood and how it all works.
There are a few things that might potentially cause bugs in your code, or waste your time (speaking from my own experience). So here goes:
1. Beware of globally keyed fragments
Let’s take example from Rails tutorial:
<% cache do %> All available products: <% Product.all.each do |p| %> <%= link_to p.name, product_url(p) %> <% end %> <% end %>
Now if you need to deal with a multi-language site you might want to make cache fragment language dependent. What might seem a convenient solution:
<%- cache([user.locale.to_s]) do -%>
will turn into a source of very interesting problems. While calling the cache method without parameters will automatically create a controller/action specific cache key, calling it with a key will make this fragment a globally keyed fragment. Cache key in the first case is going to look like “views/localhost:3000/controller-name”, and in the other case “views/en” – this is not as unique identifier any more.
While automatic cache key naming provided by rails is very convenient, it is very easy to run into a problem with duplicate cache key names used in different places.
2. Another pitfall of automatic cache key naming is that you shall never assume that when creating a cache with global key you can later find it using e.g. telnet interface to memcache. Example – add
<%- cache('unique_cache_key') do -%> <%- end -%>
in your view and then try to read directly from memcache:
$ telnet localhost 11211 GET unique_cache_key END
At the same time
GET views/unique_cache_key
will work. It’s easy to make this mistake trying to check or delete cache keys directly from memcache when using Rails cache methods.
3. delete_matched is not supported by memcached (see rails/activesupport/lib/active_support/cache/mem_cache_store.rb)
In practice that means that if you’re using memcached as Rails cache engine and trying to delete or expire fragment cache using standard Rails methods and regexp – you’ll fail.
expire_fragment(/base\/xyz.*/)
will fail miserably. Ideal solution is not to use explicit cache expiration, but rather create cache keys in such a way that doesn’t require expiration. Alternatively it’s possible to use extensions implementing delete_matched for memcached (haven’t tried it myself though).
Tip: one very useful tool for checking memcached is peep by Evan Weaver – allows you to peek into the cache and see what’s really cached and how it is used.
Leave a Reply