Guide to Caching in Rails using Memcache

Guide to Caching in Rails using Memcache 1

The post intends to cover the topics and tools that can help in implementing Memcache as caching store in Rails and debugging issues with it.

To help newbies grasp it from start, it also provides references to installing and validating the memcache install without Rails too.

"Memcached is an in-memory key-value store for small chunks of arbitrary data (strings, objects) from results of database calls, API calls, or page rendering"

Installing Memcache

  • Mac OS X Snow Leopard
    • brew install memcached
  • Ubuntu
    • sudo apt-get install memcached

More information about Installing Memcache can be found here

Starting Memcache

  • memcached -vv

-v option controls verbosity to STDOUT/STDERR. Multiple -v's increase verbosity. Memcache Settings can be inspected in detail using echo "stats settings" | nc localhost 11211. Get the thirst quenched more at http://linux.die.net/man/1/memcached or man memcached for all options.

Connecting to Memcache Server through Telnet

  • telnet localhost 11211 can be used to connect locally to run/inspect memcache data store
  • Its recommended to follow Using Memcache Commands to get a full hang of memcache commands
  • For the sake of completeness, here are three main commands (store/retrieve/remove key/values)
    • Write to Memcache (Value need to be entered in new line)
      • set abc 0 60 5 # where abc is the key, 60 is duration in seconds and 5 is length of data
      • hello # data to store in abc key
    • Read from Memcache
      • get abc
    • Delete from Memcache
      • delete abc

Implementing and Using in Rails

Installing Dalli (Memcache Client)

Configure Memcache as cache store in Rails

  • Set perform_caching and specify cache_store in the environment file (e.g. RAILS_ROOT/config/environments/production.rb)
    • config.action_controller.perform_caching = true
    • config.cache_store = :dalli_store, { :namespace => "my_project", :expires_in => 1.day, :socket_timeout => 3, :compress => true }
      • Detailed explanation of Dalli Client options can be found here

Getting Ready for the Fight/Debugging

  • IRB as always helps us get started:
    • require 'rubygems'
    • require 'dalli'
    • CACHE = Dalli::Client.new('127.0.0.1', { :namespace => "my_project", :expires_in => 3600, :socket_timeout => 3, :compress => true }) # Options are self-explanatory
    • CACHE.set('key', 'value', 180) # last option is expiry time in seconds
    • CACHE.get('key') 2
  • Memcache does not provide any command to list all keys present but at times seeing the keys and their expiry time can be a life saviour. Here is a Ruby Script to list all memcache keys

    Sample Output:

    id expires bytes cache_key
    1 2014-01-22 19:05:09 +0530 5 my_project:key
    ...

Using Memcache to store expensive calculations and avoid recalculations

  • We can store expensive calculations in memcache and retrieve it next time if expensive calculation expected to return the same result else recalculate and store again.
  • Rails provides helpers like Rails.cache.read to read cache, Rails.cache.write to write in cache and Rails.cache.fetch to return result if present in cache else write in cache and return the result
  • Code Snippet to pass a block to evaluate if key not present in cache, else return the value of key

    
    
  • Using data_cache helper created in last step in application_controller.rb

    
    

Clearing Cache

Auto-Expire after a specified time

  • Memcache accepts an expiration time 3 option with the key to write and only returns the stored result if key has not expired yet, else returns nothing.
    • Rails.cache.write(key, value, {expires_in: time})
    • Rails.cache.fetch(key, {expires_in: time}) { # Block having expensive calculations }

Faking Regex based Expiry

  • Unfortunately, Memcache does not allow listing of all keys so, apparently, there is no way to partially expire a subset of keys. Memcache only provides a delete method for the specified key restricting us to, beforehand, know the key to delete.
  • But, there is a nice trick to partially expire a subset of keys Faking Regex Based Cache keys in Rails 4.
    • To achieve it, we have to focus more on keyword subset and defining subset is the Nirvana to crack the problem.
    • Introduce a dynamic component in all the keys which we want to treat as subset
    • Have a way to control the change of dynamic component
    • When dynamic component changes, a new key is created and stored in Memcache which would be referred for data from now.
    • The old key still remains in memcache and has still not expired but it stores the stale data and fresh data is hold by new key which we are referring in all of our interactions.
    • Memcache would eventually kick the old key with stale data out (uses LRU algorithm to allocate space for new data)
    • In the snippet below, we have added dynamic component last_cache_refreshed_at in Category which can be selectively changed to expire cache of particular categories instead of all

      
      

Writing Test Cases

Gotchas

  • Using touch command to update expiry cache time (Fixed in Memcache 1.4.14)
  • Check Memcache Version using telnet client
    • Execute stats
    • Look for STAT version 1.4.13

  1. Caching Expensive Api Requests Results

  2. If Object is saved in Memcache, Cache.get('key') might raise

    Dalli::UnmarshalError: Unable to unmarshal value: undefined class/module <model_name>

    Use require_dependency "app/models/.rb"

  3. Expiry Time of 0 makes the key stays for indefinite time only limited by Memcache clearing it out in case of cleaning up

  4. Scoped Expiration AND Reload Namespace using a Proc


SSL checklist for Ruby on Rails Applications


The purpose of SSL is to provide a reasonable level of protection against eavesdropping and man-in-the-middle attacks. Although SSL provides a greater level of security, it introduces a lot of overheads and hence should be used sparingly. Two of the most common places to use SSL is for payment transactions and user registration/login.
This post intentionally focuses only on the Rails application as there are numerous post on the net for SSL setup on the server. Enabling SSL in a Rails application is really trivial and there are just a few points that need your attention..
Read more


Ruby on Rails Caching And JavaScript Techniques

Cross posted from darthsid

While implementing caching in a recent rails project I came across some typical caching issues. In a lot of pages the content is same for all users but certain components in them have user specific actions. As an example, I have a page listing all public messages that users have posted(similar to the public timeline in twitter) but actions on those messages are user specific(eg: only owner or admin can delete a message). Also, most of these actions use ajax and the rails authenticity token in them also gets cached resulting in subsequent failures if the session changes. Another issue was that the timestamps in most pages is fuzzy and they become irrelevant if a page gets cached for too long. I could have created separate caches for each user but if the user base really grows managing the caches would become a nightmare and that would still not solve the authenticity token and the timestamp problem. The simplest solution was to use JavaScript, more specifically jQuery.
Read more


Git Work Flow For Ruby on Rails Developers

This is my very first blog post and so I thought it should be about a tool that is indispensable for me - Git. I started using git about 10 months ago and looking back I can't imagine how I managed to get work done without it. The purpose of this post, however, is not to sing git's praises, there are lots of good articles on the web that do so much better than I ever could. Instead, I wish to share the work-flow I use on my projects. I developed this work-flow by trial and error over the months and is currently the most efficient and productive approach I can think of. If any experienced git users happen to stumble upon this post, please do provide suggestions/alternatives to help me improve my process.

The project I am currently working on requires me to maintain two parallel deployment branches. One is a "production branch" which is deployed on the live server and the other is a "development branch" which is deployed on a staging server. All enhancements and feature additions are done in the "development branch" and the only changes made in the "production branch" are production bug fixes that need urgent attention. Once the "development branch" is deemed stable it is merged into "production branch" and deployed.
Read more


New Ruby on Rails Plugin- Launching Soon has launched

"Launching Soon" is a new plugin that VinSol has developed.

Working on client projects, we saw a pattern - the client owns a domain, which is either parked or has a blog running on it, and then they commission us to build an application. While the application is being developed, they want us to setup a landing page on the server, with a couple of links - to the blog etc. and want to capture email addresses of people who are interested in the service that the application would offer.

We actually found a few ready to use options for this. This php based paid script was one and there is also a  free launching soon module in PHP/Jquery.

However, we did not find anything for Rails so Satish set out to write a plugin for it. Then we thought it would be good to integrate it with Campaign Monitor , since most of our clients use this service for running their email campaigns.  And subsequently Satish extended it to add Mail Chimp integration also.

Give it a spin and send us your feedback via the LightHouse project.