title: Improving Rails.cache.write by setting key-value pairs asynchronously
body: <p>I am currently thinking about improving the performance of <code>Rails.cache.write</code> when using dalli to write items to the <a href="https://addons.heroku.com/memcachier" rel="nofollow">memcachier</a> cloud. </p>
  
  <p>The stack, as it relates to caching, is currently:</p>
  
  <p><code>
  heroku, memcachier heroku addon, dalli 2.6.4, rails 3.0.19
  </code></p>
  
  <p>I am using newrelic for performance monitoring.</p>
  
  <p>I am currently fetching "active students" for a given logged in user, represented by a <code>BusinessUser</code> instance, when its <code>active_students</code> method is called from a controller handling a request that requires a list of "active students":</p>
  
  <pre><code>class BusinessUser &lt; ActiveRecord::Base
    ...
    def active_students
      Rails.cache.fetch("/studio/#{self.id}/students") do
        customer_users.active_by_name
      end
    end
    ...
  end
  </code></pre>
  
  <p>After looking at newrelic, I've basically narrowed down one big performance hit for the app in setting key values on memcachier. It takes an average of <strong>225ms</strong> every time. Further, it looks like setting memcache key values blocks the main thread and eventually disrupts the request queue. Obviously this is undesirable, especially when the whole point of the caching strategy is to <em>reduce</em> performance bottlenecks.</p>
  
  <p>In addition, I've benchmarked the cache storage with plain dalli, and Rails.cache.write for 1000 cache sets of the same value:</p>
  
  <pre><code>heroku run console -a {app-name-redacted}
  irb(main):001:0&gt; require 'dalli'
  =&gt; false
  irb(main):002:0&gt; cache = Dalli::Client.new(ENV["MEMCACHIER_SERVERS"].split(","),
  irb(main):003:1*                     {:username =&gt; ENV["MEMCACHIER_USERNAME"],
  irb(main):004:2*                      :password =&gt; ENV["MEMCACHIER_PASSWORD"],
  irb(main):005:2*                      :failover =&gt; true,
  irb(main):006:2*                      :socket_timeout =&gt; 1.5,
  irb(main):007:2*                      :socket_failure_delay =&gt; 0.2
  irb(main):008:2&gt;                     })
  =&gt; #&lt;Dalli::Client:0x00000006686ce8 @servers=["server-redacted:11211"], @options={:username=&gt;"username-redacted", :password=&gt;"password-redacted", :failover=&gt;true, :socket_timeout=&gt;1.5, :socket_failure_delay=&gt;0.2}, @ring=nil&gt;
  irb(main):009:0&gt; require 'benchmark'
  =&gt; false
  irb(main):010:0&gt; n = 1000
  =&gt; 1000
  irb(main):011:0&gt; Benchmark.bm do |x|
  irb(main):012:1*   x.report { n.times do ; cache.set("foo", "bar") ; end }
  irb(main):013:1&gt;   x.report { n.times do ; Rails.cache.write("foo", "bar") ; end }
  irb(main):014:1&gt; end
         user     system      total        real
   Dalli::Server#connect server-redacted:11211
  Dalli/SASL authenticating as username-redacted
  Dalli/SASL: username-redacted
    0.090000   0.050000   0.140000 (  2.066113)
  
  Dalli::Server#connect server-redacted:11211
  Dalli/SASL authenticating as username-redacted
  Dalli/SASL: username-redacted
  
    0.100000   0.070000   0.170000 (  2.108364)
  </code></pre>
  
  <p>With plain dalli <code>cache.set</code>, we are using 2.066113s to write 1000 entries into the cache, for an average <code>cache.set</code> time of 2.06ms.</p>
  
  <p>With <code>Rails.cache.write</code>, we are using 2.108364s to write 1000 entries into the cache, for an average <code>Rails.cache.write</code> time of 2.11ms.</p>
  
  <p>⇒ It seems like the problem is not with memcachier, but simply with the amount of data that we are attempting to store.</p>
  
  <p>According to <a href="http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html#method-i-fetch" rel="nofollow">the docs for the #fetch method</a>, it looks like it would not be the way I want to go, if I want to throw cache sets into a separate thread or a worker, because I can't split out the <code>write</code> from the <code>read</code> - and self-evidently, I don't want to be reading asynchronously.</p>
  
  <p>Is it possible to reduce the bottleneck by throwing <code>Rails.cache.write</code> into a worker, when setting key values? Or, <strong>more generally</strong>, is there a better pattern to do this, so that I am not blocking the main thread every time I want to perform a <code>Rails.cache.write</code>?</p>