Forum: Ruby on Rails model.reload not working; preventing me from rescuing optimistic lock exception

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
gaveeno (Guest)
on 2009-03-08 12:33
(Received via mailing list)
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
gaveeno (Guest)
on 2009-03-08 12:39
(Received via mailing list)
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)
Frederick C. (Guest)
on 2009-03-08 14:59
(Received via mailing list)
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
gaveeno (Guest)
on 2009-03-08 21:20
(Received via mailing list)
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>
This topic is locked and can not be replied to.