Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Save a record within transaction even if transaction rolls back?

I have a series of records to save as part of a transaction. In addition to normal AR records, I am doing a credit card gateway transaction. If it or the AR transactions fail, I want everything rolled back ... except for the data of the failed transaction (reason, date, etc.) that I get back from the credit card gateway. Something like

def pay
  Payment.transaction do
    payment.save
    other.save
    result = credit_card.purchase  # this does the gateway transaction, no database stuff
    if result.failure
      raise ActiveRecord::Rollback
      result.save  # this is the part I want to always save
    end
    another.save
  end
end

Is there a way to exclude a specific part within a transaction from rollback on failure?

Rails 3.2.5, MySQL 5.1

like image 883
Tom Harrison Avatar asked Jul 13 '12 20:07

Tom Harrison


1 Answers

I'm not 100% sure I understand why you want to do this but could you just save the credit card stuff outside of your transaction?

result = nil
Payment.transaction do
  payment.save
  other.save
  result = credit_card.purchase  # this does the gateway transaction, no database stuff
  if result.failure
    raise ActiveRecord::Rollback      
  end
end
result.save

(you need to set result to nil before the transaction because of how block variable scoping works)

Another possible strategy is to use the fact that transactions are done a per connection basis. Two threads will use different connections, so you could do:

Payment.transaction do
  payment.save
  other.save
  result = Thread.new do
    ActiveRecord::Base.connection_pool.with_connection do
      credit_card.purchase.tap do |result|
        result.save
      end
    end
  end.join.value
  if result.failure 
    raise ActiveRecord::Rollback
  end
end

or maybe just:

Payment.transaction do
  payment.save
  other.save
  result = credit_card.purchase
  Thread.new do
    ActiveRecord::Base.connection_pool.with_connection do
      result.save
    end
  end.join
  if result.failure 
    raise ActiveRecord::Rollback
  end
end

Here the purchase happens on another thread, i.e. with its own database connection. Anything that happens in that thread won't be rolled back

like image 91
Frederick Cheung Avatar answered Nov 10 '22 09:11

Frederick Cheung