Michael Kovacs' photos More of Michael Kovacs' photos
Recommend Me Cable Car Software logo

Tuesday, May 01, 2007

Rails realities part 25.1 (Do what you say, but say the right thing)

After yesterday's post about fine grained column updates with update_attribute I was discussing the matter with Sam Pullara and Patrick Linskey with regards to how Hibernate and Kodo deal with these kinds of updates.

First off as a reminder of the data and scenarios I'm talking about:
class Reservation < ActiveRecord::Base

# end user action
def edit_reservation
reservation = Reservation.find(params[:id])

# admin only action
def confirm_reservation
reservation = Reservation.find(params[:id])
reservation.update_attribute('confirmed', true)

The desire is to be able to update the "confirmed" field without saving the rest of the record. The update_attribute that is part of ActiveRecord::Base updates the field and saves the whole record which seems wrong. I wanted to be able to "cheat" and not have to add either optimistic concurrency or a new table to support the notion of confirming a reservation and only allowing an admin user to do so.

Speaking with Patrick he said that Kodo doesn't allow for explicit partial object flushing as it is fraught with peril. He asserted that optimistic concurrency is the only way to go and it's really cheap. He actually went as far as to suggest always having an optimistic lock column on all tables.

Yesterday I said that I felt it was too heavy handed but as I was lying in bed last night at my new apartment, kept awake by the unknown man living above me clearing his throat with great vigor every 20 minutes or so, just long enough intervals to doze off and be abruptly awoken at the next "clearing", fun... What was I saying? Oh right, heavy handed optimistic concurrency.

After thinking about it more I realized that Patrick was right (Wow imagine that, the guy who's been focused on O/R mapping for the past 6 years or so :-). Even doing fine grained updates like I talked about yesterday isn't a good solution to my particular problem and is probably something that you shouldn't be doing at all unless you know that you're never going to have concurrent editing (and we know how often application requirements change so this probably isn't a good assumption to make and bake into your app). I was so concerned with not forcing the end user to deal with OC errors and thinking "well I just need to update this flag that's not edited by the user at all", that I failed to remember that because AR always saves all values that I still have a race condition, only the other way now, where a user could potentially change the "confirmed" value that I've so carefully set with my tricked out update_attribute method.

So my choices really are:

1) Add an optimistic lock to the table which has the undesired effect of possibly forcing a user to retry their edit, or
2) Create a new table for the "confirmed" flag which would only be editable by the admin users.

I think that for my case I'm going to add a new table because I really don't think it's appropriate to either have end users retry an operation that should always work, nor do I think it's a good policy to just try again on their behalf if it fails the first time. The "confirmed" flag is really an admin function and only used internally to the company so while it feels like a bit much to create a table for just that flag I may have to, but will reevaluate if there's an appropriate design that's a little more practical and not so "wasteful".

Anyway, for the folks that are doing single field updates, don't believe that the patch I created is something that's going to safeguard you against concurrency because it won't. It's the wrong solution. The update_attribute ability doesn't even exist on other O/R mappers and I can see why more clearly now. They get around a problem like this by offering users the ability to define lock groups, where only certain fields are used for optimistic concurrency, and more.

ActiveRecord isn't as sophisticated so if you're going to use it know your limitations in terms of what you should or shouldn't do when designing your schema as it relates to your app's requirements. So in the end it really doesn't matter that update_attribute saves the entire record by default because if you're desiring a fine grained update you're likely asking for trouble at some point. Time to unhack my hack :-)


Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?