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.
F05df05d7e52f4ad93cd0f9027818c75?d=identicon&s=25 gaveeno (Guest)
on 2009-03-08 11: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
F05df05d7e52f4ad93cd0f9027818c75?d=identicon&s=25 gaveeno (Guest)
on 2009-03-08 11: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)
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-03-08 13: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
F05df05d7e52f4ad93cd0f9027818c75?d=identicon&s=25 gaveeno (Guest)
on 2009-03-08 20: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 Cheung <frederick.che...@gmail.com>
This topic is locked and can not be replied to.