Friday, March 12, 2010

Rails "Good Touch"/"Bad Touch"


Rails is bit like a dirty old man.

Consider this example
class User < ActiveRecord::Base
  after_save :do_something_expensive
end

Rails makes it trivial to update timestamps, and I found out the hard way that the underlying implementation leaves a lot to be desired.
User.first.touch

Easy, right? But, did you know that this useful little method will fire all of your after_save callbacks?  This led to some nasty performance problems recently, and now you can now stop Rails from having it's way with you!  Bad Touch Rails!

The good_touch project is now available on github and gemcutter and eliminates the overhead for updating a simple timestamp attribute.  It's as easy as using the standard/bad touch method without all the horseplay.
User.first.good_touch

Let the touching continue...

4 comments:

  1. I almost never use after_save, as that alone can be taxing. But I agree, touch should not invoke any callbacks.

    ReplyDelete
  2. I actually really like the fact that #touch invokes callbacks. I use it when I just want to invoke the callbacks on a record (say, in a rake task that calls it for old records that were created before you added a particular callback that does something important). I'm not sure how useful it is to just change updated_at on its own.

    ReplyDelete
  3. It seems obvious to me that "touch" should cause the object to saved even though nothing changed. Updating the timestamps is side effect. Surely it's also a common reason to use touch, but it's not the main point or anything.

    Paring it down to not even use the save callbacks?! It seems to me the "bad" part isn't #touch at all, but your concept of what after_save means. Maybe you shouldn't be using after_save at all, if you're doing expensive stuff that doesn't have to do with the object being saved.

    I often see after_save used where it should have been a fancy setter that did extra work, instead. People do it because they have this newfangled idea that more magic is better than less magic. Really though magic is only good when standards, hardware, interoperability or some other demon causes a bunch of required ugly; then you want to magically hide that required ugly. Otherwise, magic is just a synonym for "clever use of side effects."

    "Look, it does fancy expensive stuff automagically after I save! Isn't that great?!" And this is really what people are telling themselves, and even admitting out loud, after writing it. It should rather make them scream though. "Do the expensive stuff explicitly, it's worth a couple bytes!"

    And if you're having trouble with your expectations of #touch, maybe you were too clever after all.

    ReplyDelete
  4. A perfect example of this updating search index. This is a very common after_save event, and triggering this when you just want to update a timestamp really sucks.

    This is the exact usecase where good_touch rocks!

    ReplyDelete