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 Olszowka

Christoph Olszowka 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.

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

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.

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