Record locking

Looking through the api on record locking I’m confused.

given the stateless nature of html, it’s entirely possible that someone
might click on ‘edit’ and then simply abandon their ‘edit’ functions
without ever saving - which tells me that I should just have a column in
each table called ‘lock_version’ with a default of 0 - (Opportunistic
locking) right?

Then I need a method in each model employing record locking to rescue
any exceptions raised by multiple people trying to edit without saving
and in essence checking out the data again and discarding their edits.

Do I have this right?

Craig

On Apr 21, 2008, at 10:43 AM, Craig W. wrote:

Then I need a method in each model employing record locking to rescue
any exceptions raised by multiple people trying to edit without saving
and in essence checking out the data again and discarding their edits.

Do I have this right?

Craig

Yes, you are right. It is dangerous to leave a record locked any
longer than necessary, and very dangerous to leave it locked if you
don’t absolutely know the transaction will complete. If you want
optimistic locking, add a column called lock_version. If you want
pessimistic locking, see this:

http://ryandaigle.com/articles/2006/6/27/whats-new-in-edge-rails-pessimistic-locking

It’s safe to use optimistic locking across http requests if you don’t
mind failed updates when collisions occur. Otherwise, consider
pessimistic, as described.

G’luck!

On Mon, 2008-04-21 at 11:08 -0700, s.ross wrote:

each table called ‘lock_version’ with a default of 0 - (Opportunistic
Yes, you are right. It is dangerous to leave a record locked any
longer than necessary, and very dangerous to leave it locked if you
don’t absolutely know the transaction will complete. If you want
optimistic locking, add a column called lock_version. If you want
pessimistic locking, see this:

http://ryandaigle.com/articles/2006/6/27/whats-new-in-edge-rails-pessimistic-locking

It’s safe to use optimistic locking across http requests if you don’t
mind failed updates when collisions occur. Otherwise, consider
pessimistic, as described.


I’m surprised that this gets so little discussion.

Clearly pessimistic locking is not where I want to go (too many
untrained users).

This rescue must be built into each model’s ‘update’ function I
gather…I think I get it.

Thanks

Craig

On Mon, 2008-04-21 at 10:43 -0700, Craig W. wrote:

and in essence checking out the data again and discarding their edits.


OK - something must be wrong with my controller code then.

I added ‘lock_version’ to my ‘clients’ table and restarted my mongrels.

in script/console, opportunistic locking works as advertised in the API

but using 2 separate browsers (1 Firefox, 1 Konqueror) with 2 separate
sessions, I can update either in any order without an issue and the
updates are saved and the ‘lock_version’ value simply increments.

this is my controller code…can you see how I might be defeating
opportunistic locking?

id = params[:id]
if id && @client.valid?
begin
@client.update_attributes(params[:client])
flash[:notice] = ‘Client was successfully updated.’
redirect_to :back
rescue
flash[:notice] = “Client record was changed by someone else
while you were editing and you will have to edit again,
starting over”
redirect_to :back
end
else
flash[:notice] = “Danger Will Robinson”
redirect_to :back
end

Thanks

Craig

On Mon, 2008-04-21 at 15:42 -0700, Craig W. wrote:

Then I need a method in each model employing record locking to rescue
sessions, I can update either in any order without an issue and the
redirect_to :back
rescue
flash[:notice] = “Client record was changed by someone else
while you were editing and you will have to edit again,
starting over”
redirect_to :back
end
else
flash[:notice] = “Danger Will Robinson”
redirect_to :back
end


never mind…I see why this is happening…first step in my ‘update’
method retrieves the current value of ‘lock_version’ from the record. I
had to submit the value of lock_version in the params hash as a
hidden_field.

Duh…

Craig

On 21 Apr 2008, at 23:42, Craig W. wrote:

sessions, I can update either in any order without an issue and the
updates are saved and the ‘lock_version’ value simply increments.

This is intended. Opportunistic locking guards you from changes
happening behind your back in between you doing foo = Foo.find(123)
and foo.save
Pessimistic locking has a similar sort of the database transaction
it’s enclosed in, and you wouldn’t want that lasting beyond a single
request (apart from anything else, if you have more than 1 mongrel,
there’s no guarantee you won’t get some requests served by 1 mongrel
and one by the other).

It sounds as though you’re worried about the record changing in
between the user viewing the form and you saving the record. There’s
no support for that builtin, but I think you could extend the
principle of optimistic locking (include the current lock_version as a
hidden field on the form)

Fred

On Tue, 2008-04-22 at 00:11 +0100, Frederick C. wrote:

It sounds as though you’re worried about the record changing in
between the user viewing the form and you saving the record. There’s
no support for that builtin, but I think you could extend the
principle of optimistic locking (include the current lock_version as a
hidden field on the form)


yeah - I sort of figured that out (the hidden field on the form sending
back the lock_version value that it checked out) but this causes me a
problem with validations now.

Here’s the issue in simplistic form…

controller code…

before optimistic locking

if @client.update_attributes(params[:client])
flash[:notice] = “Bravo”
redirect_to :back
else
flash[:notice] = @client.validate
redirect_to :back
end

NOW, with opportunistic locking…

if whatever
begin
@client.update_attributes(params[:client])
flash[:notice] = “Bravo”
redirect_to :back
rescue
flash[:notice] = “Something changed in the interim”
redirect_to :back
end
end

The validate is lost in the rescue…how do I get my cake too?

Thanks

Craig

On Mon, 2008-04-21 at 16:35 -0700, Craig W. wrote:

back the lock_version value that it checked out) but this causes me a
redirect_to :back
flash[:notice] = “Bravo”
redirect_to :back
rescue
flash[:notice] = “Something changed in the interim”
redirect_to :back
end
end

The validate is lost in the rescue…how do I get my cake too?


I guess the question I am asking is if I have

@client
and params[:client]

how do I validate @client with params[:client] modifications prior to
saving it?

Craig

Craig -

On 21-Apr-08, at 7:50 PM, Craig W. wrote:

hidden field on the form)

NOW, with opportunistic locking…
end

Craig

@client.attributes = params[:client]
@client.valid?

cheers,
Jodi

On Mon, 2008-04-21 at 19:53 -0400, Jodi S. wrote:

between the user viewing the form and you saving the record. There’s

flash[:notice] = @client.validate
rescue
and params[:client]

how do I validate @client with params[:client] modifications prior to
saving it?

Craig

@client.attributes = params[:client]
@client.valid?


shameful of me to be working with Rails for 2 years now and I never
knew…

@client.attributes = params[:client]

was possible

Thanks

Craig

Jodi S. wrote:

@client.attributes = params[:client]
@client.valid?

isn’t

@client.update_attributes params[:client]

a shortcut for the example above ?

Lionel

On 22 Apr 2008, at 09:45, Lionel B. wrote:

a shortcut for the example above ?
No, since update_attributes actually performs the save, whereas valid?
will just run the validations.

Fred

Frederick C. wrote:

[…]
No, since update_attributes actually performs the save, whereas valid?
will just run the validations.

Whoops. I had a feeling my question was stupid. Still need coffee…

On Tue, 2008-04-22 at 12:01 +0100, Frederick C. wrote:

@client.update_attributes params[:client]

a shortcut for the example above ?
No, since update_attributes actually performs the save, whereas valid?
will just run the validations.


which of course is a problem once you introduce begin/rescue code into a
‘save’ function because the validation results get swallowed by the
rescue.

I have one particular controller that does a bunch of hoops and I
started by doing this…

if params[:placement][:lock_version].to_i != @placement.lock_version
flash[:notice] = “Placement record was changed by someone else”
redirect_to :back
return false
end

which as far as I can tell, still leaves a tiny sliver of time for
someone to actually update this same record before the ‘save’ code is
executed but in this model, I’m willing to take the risk because there
are so few people who have write access to this model and the likelihood
of two people editing the same record at the same time is really, really
small. (yeah I know…transactions)

I’m glad I finally took a look at record locking but it does complicate
things.

Thanks all

Craig