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

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

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)

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.

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

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