Model.reload not working; preventing me from rescuing optimistic lock exception


#1

This error’s driving me crazy…

I’m trying to test the code that I wrote to rescue the
ActiveRecord::StaleObjectError exception…this is the exception that
can occur when a model with optimistic locking is updated by multiple
processes concurrently. For some reason, though, model.reload doesn’t
seem to want to actually reload the model from the db when I rescue
the exception.

This is the code I’m using:

Model

class SmsTxn < ActiveRecord::Base

after_create :update_credits

def update_credits

return unless self.company
credit_balance ||= self.company.credit_balance

## using ruby-debug, i stopped the code here and executed
## the commands below (using script/console)

credit_balance.used_credits += 1
credit_balance.save
credit_balance.used_credits

rescue ActiveRecord::StaleObjectError

credit_balance.reload

## i stopped the code here and saw that credit_balance didn't

reflect the
## changes made in the console (see below) even though I checked
via SQL
## and the database DID reflect the update.

retry

end
end

code executed in the console after credit balance was loaded by

the update_credits method above
c = CreditBalance.find(1) # 1 is the id of the company whose credit
balance
# I’m testing the code above
with

c.update_attribute(:used_credits,999) # I assigned a random number for
used_credits

For some reason this is causing an infinite
loop…credit_balance.reload is not reloading the model from what’s in
the database so when it retries it gives the same error. I first
tried this test by updating the value directly via SQL, but that gave
me the same problem so I thought that maybe the way optimistic locking
works it wasn’t recognizing the change because it was outside of the
Rails framework.

Anybody know what I’m doing wrong??

Thanks!
Gavin


#2

On 8 Mar 2009, at 10:39, gaveeno wrote:

I’m not entirely sure if this is what you are asking, but the
before_, save, after sequence all runs in one transaction. You
should read up on transaction isolation levels (ie what changes from
other transactions a given transaction sees)

Fred


#3

Here’s the code again, just formatted slightly better

Model

class SmsTxn < ActiveRecord::Base

after_create :update_credits

def update_credits

return unless self.company

credit_balance ||= self.company.credit_balance

## using ruby-debug, i stopped
## the code here and executed
## the commands below (using
## script/console)

credit_balance.used_credits += 1
credit_balance.save
credit_balance.used_credits

rescue ActiveRecord::StaleObjectError
credit_balance.reload

## i stopped the code here and
## saw that credit_balance didn't
## reflect the changes made in
## the console (see below) even though
## I checked via SQL and the
## database DID reflect the update.

retry

end
end

code executed in the console

after credit balance was loaded by

the update_credits method above.

1 is the id of the company whose credit

balance I’m testing the code above with.

I updated used_credits with a random num.

c = CreditBalance.find(1)
c.update_attribute(:used_credits,999)


#4

Hey Fred,

Thanks for the input. I didn’t realize they all run in one
transaction, so that explains why my seemingly logical approach didn’t
work.

I ended up using pessimistic locking:

def update_credits(credits=1)
return unless self.company
credit_balance ||= CreditBalance.first(:conditions => “company_id
= #{self.company.id}”,:lock => true)
credit_balance.used_credits += credits
credit_balance.save!
rescue Exception
credit_balance.reload(:lock => true)
retry
end

Since I’ll always be using the update_credits to update a company’s
credits used I can’t foresee using pessimistic locking being a
problem.

Thanks again,
Gavin

On Mar 8, 5:58 am, Frederick C. removed_email_address@domain.invalid