Optimistic Locking in Controllers: Best practice?

Hi,

I’ve searched the group archives on this, but could not find anything
except an unanswered question that results from the same problem I
encountered, or at least that’s what I guess (http://groups.google.com/
group/rubyonrails-talk/browse_thread/thread/d771abdd51e12c8d/
68d54f84c00fa45a).

The basic theory with locking is: Take two objects, save one of those,
and saving of the second one will fail. Sounds nice and works very
well in console, which is being demonstrated in the Rails cookbook
etc.

BUT: When I want to do such a thing in a controller action it won’t
work the way it’s supposed to (at least imho), because I am not
retrieving the object from the form (say “edit”) and re-using it, but
rather retrieving a new instance by id with a before_filter get_user
or whatever. Now if a second user saves the same object after I
accessed the form, but before I submit my changes, his changes will be
overwritten by mine without a StaleObjectError being risen, because
the lock_version seems correct.

This really is NOT what I want locking to do for me. My aim is to
prevent users from submitting forms with outdated data.

So now I came up with the idea of adding a hidden input “lock_version”
to my forms and applying this param to my model instance in “update”.
Now locking will work properly, but this does not seem very clean as
the user could just manipulate those data or remove the lock_version
entirely, which would skip the locking protection.

Is there something like a “best practice” for implementing such a
locking behavior in controllers? I’ve been thinking about checking if
the lock_version is given in params with a before_filter or something,
as well as storing instances of the model in session, but as I
mentioned, this does not seem pretty clean code to me, so now I am
hoping for someone who already thought more about this topic to help
me out :slight_smile:

Thanks in advance and greetings,
Christoph O.

Christoph O. wrote:

So now I came up with the idea of adding a hidden input “lock_version”
to my forms and applying this param to my model instance in “update”.
Now locking will work properly, but this does not seem very clean as
the user could just manipulate those data or remove the lock_version
entirely, which would skip the locking protection.

If your users wants to corrupt your data, optimistic locking is not
going to solve the problem for you. Optimistic locking is not a security
mechanism. Even if you did ensure that a user could not save stale data
the user could just refetch the object from the database and update it
again with whatever changes desired.

If you really, really want to ensure that no user ever submits stale
data you could use a hash for comparison and record a new hash value in
the record on each update. In that case you can ensure that no-one will
ever be able to submit a stale record.


Cheers,

  • Jacob A.

Hi Jacob,

I know that this kind of locking won’t give me perfect safety, but
I am rather aiming for some user convenience anyway. The main
purpose should be to tell the user “Whoops, someone was quicker
than you!” and throw him back to the page, rather than ensuring
really perfect data consistency.

Greetings
Christoph

On 6/12/07, Christoph O. [email protected] wrote:

Hi,

So now I came up with the idea of adding a hidden input “lock_version”
to my forms and applying this param to my model instance in “update”.
Now locking will work properly, but this does not seem very clean as
the user could just manipulate those data or remove the lock_version
entirely, which would skip the locking protection.

Given the amount of protection desired (convenience only, not
security), this seems like a perfectly reasonable approach to me, and
it’s much better than storing it in the session because it makes the
post stateless.

/Nick

On 6/12/07, Christoph O. [email protected] wrote:

So now I came up with the idea of adding a hidden input “lock_version”
to my forms and applying this param to my model instance in “update”.
Now locking will work properly, but this does not seem very clean as
the user could just manipulate those data or remove the lock_version
entirely, which would skip the locking protection.

That’s why we have serer-side sessions. Save the lock_version value
there. Do not save the entire model object though, storing model
instances in the session is not just ugly, it’s an almost certain way
to introduce interesting, unreproducible bugs.


Alex Verkhovsky
RubyWorks