Development and UX from Michael Mahemoff. Maker of Player FM. Previously: Google, BT, O'Reilly author.

Menu

Tag Archives: Sweeper

As you’ll see here, Rails cache sweepers are a tricky subject. Here are some general things I’ve learned.

Sweepers are dual creatures. “Here’s the scoop: sweepers observe both your models and your controllers. They’re not half-this and half-that, they’re both_. You can define model callbacks (after_save, afterupdate, etc.), and you can also define before/after callbacks for any controller action (e.g. after_list_create).”

Notwithstanding the above, most references and despairing workarounds focus on their controller nature. Dandy for a web forms app, but in my case, cached content is being invalidated by daemon processes operating directly on models.

There’s no standard home for Sweepers. So much for convention-over-configuration :). So I opted for a new app/sweepers directory and added it in application.rb: config.autoload_paths += %W(#{config.root}/app/sweepers).

Now to the crux of the matter: The Sweeper still does nothing, even if it’s in the path. I don’t know why, but it’s a common problem! I had to explicitly add it as an observer: config.active_record.observers = :episode_sweeper. This is the model equivalent of people explicitly adding it to their controller with an after_update hook.

Now to the crux of the matter, redux: Okay, so now the sweeper is being called when models change (specifically, the models it declares it’s observing). Great. But it still doesn’t work — expire_fragment apparently doesn’t, because I’m still seeing the the old fragment appear in the web app. WAT? The answer turned out to be, don’t just call expire_fragment(). Instead, call ActionController::Base.new.expire_fragment(). It seems the fragment used when outputting a view is not the same as that expired by expire_fragment(). I’m only telling you what worked here, I can’t tell you why!

You can also expire cache in the Rails console for testing purposes, just call ActionController::Base.new.expire_fragment(). (I think you need to restart Rails (and the console) if you update the sweeper code, given that it’s set up as an arel observer in the config line above. But haven’t fully tested that.)

This is just a basic implementation for now. A better implementation is probably to use DHH’s key-based caching approach, which has the neat principle of generating a new key every time the fragment changes.

Share this:

G’Day

Welcome to Michael Mahemoff's blog, soapboxing on software and the web since 2004. I'm presently using HTML5 and the web to make podcasts easier to share, play, and discover at Player FM. I've previously worked at Google and Osmosoft, and built the Ajax Patterns wiki and corresponding book, "Ajax Design Patterns" (O'Reilly 2006).
For avoidance of doubt, I'm not a female, nor ever have been to my knowledge. The title of this blog alludes to English As She Is Spoke, a book so profoundly flawed it reminded me of the maturity of the software industry when this blog began in 2004. I believe the industry has become more sophisticated since then, particularly the importance of UX.
Follow @mahemoff