<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:ent="http://www.purl.org/NET/ENT/1.0/" version="2.0">
  <channel>
    <title>Curiouser and Curiouser! on caching</title>
    <link>http://matt.blogs.it/</link>
    <description>RSS feed for topic caching</description>
    <copyright>Copyright 2009 Matt Mower. Some rights reserved.</copyright>
    <generator>Squib/0.5.0.382</generator>
    <managingEditor>self@mattmower.com</managingEditor>
    <webMaster>self@mattmower.com</webMaster>
    <language>en-gb</language>
    <item>
      <title>Expiring Rails cache from a background task</title>
      <link>http://matt.blogs.it/entries/00002974.html</link>
      <pubDate>Fri, 13 Feb 2009 22:05:00 +0000</pubDate>
      <description>&lt;p&gt;One of the great things about switching to &lt;a href="http://github.com/tobi/delayed_job/tree/master"&gt;Delayed Job&lt;/a&gt; for background processing in &lt;a href="http://reeplay.it/"&gt;reeplay.it&lt;/a&gt; is that it's made it trivial to take tasks out of the request pipeline and move them into the background.&lt;/p&gt;

&lt;p&gt;One such came up today. Because &lt;a href="http://blog.quintarelli.it/"&gt;Stefano&lt;/a&gt; makes friends with just about every user in the system a particular update reached the point where it was taking so long to run that we were getting gateway timeouts... bad news.&lt;/p&gt;

&lt;p&gt;But we knew the answer almost immediately. Move that part of the request into a DJ task. All too easy...&lt;/p&gt;

&lt;p&gt;... except that part of the task used a cache sweeper to. Oops!&lt;/p&gt;

&lt;p&gt;Rails caching is neat but it seems to be, and for reasons that escape me, inextricably linked to the ActionController context. I've hit this problem before and the usual compromises are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a special controller action just to expire the cache&lt;/li&gt;
&lt;li&gt;Use your knowledge of where the cache is and how it's stored to delete it by hand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my money both of the above suck. What I really want is to be able to access the cache from anywhere in the Rails stack. Today I snapped and figured out how to do that.&lt;/p&gt;

&lt;p&gt;Here's a cut down version of what my DJ job class looked like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class VideoJob &amp;lt; Job

    def job
          video.do_something_awesome
          sweep_video_cache( video )
    end

    def do_something_awesome
      # apply secret sauce
    end

    def sweep_video_cache( video )
      # err, what do we do here?
    end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;now I add just enough &lt;code&gt;ActionPack&lt;/code&gt; "goodness" to make the &lt;code&gt;Fragments&lt;/code&gt; module feel at home:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class UpdateVideoStatus &amp;lt; Job
  include ActionController::Caching::Fragments

  def job
    video.do_something_awesome
    sweep_video_cache( video )
  end

  def do_something_awesome
      # apply secret sauce
    end

  def sweep_video_cache( video )
    content = video.content
    expire_fragment( "user/#{content.user.id}/content/#{content.id}" )
    expire_fragment( "user/#{content.user.id}/content_tiled/#{content.id}" )
    expire_fragment( "user/#{content.user.id}/dashboard/recent_video" )
    expire_fragment( "user/#{content.user.id}/dashboard/river_of_video" )
  end

  private

  # The following methods are defined to fake out the ActionController
  # requirements of the Rails cache

  def cache_store
    ActionController::Base.cache_store
  end

  def self.benchmark( *params )
    yield
  end

  def cache_configured?
    true
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By adding three methods we're able to call &lt;code&gt;expire_fragment&lt;/code&gt; just like we would from a cache sweeper. This is working for me using the FileStore but I don't see any reason it shouldn't work using other kinds of store.&lt;/p&gt;

&lt;p&gt;My original hope had been that I could use our existing cache sweeper but it looked more complicated to get that working outside of the controller. This was the compromise. I'll refactor to have them share code later.&lt;/p&gt;

&lt;p&gt;I don't think I'm doing anything too heinous here but if there's a better way I'd love to hear it.&lt;/p&gt;</description>
      <guid isPermaLink="true">http://matt.blogs.it/entries/00002974.html</guid>
      <ent:cloud ent:href="http://matt.blogs.it/topics/">
      </ent:cloud>
    </item>
  </channel>
</rss>
