如何知道为什么交易被回滚?

时间:2018-08-22 04:09:30

标签: ruby-on-rails activerecord transactions

我有一个Account模型。它具有通常的validationsafter_save回调。有一个要求,并且将根据某些其他验证策略来创建帐户对象。我有以下代码片段,效果很好:

def special_account_creation
  Account.transaction do
    a = Account.create(params)
    resp = update_third_party(a) # Throws exception if unable to update
    validate_amount(a, resp)     # Throws exception if `a` is invalid
  # rescue Rollback ??? <--- How to do this
  #   msg = <transaction_error_details>
  # Rails.logger.info("Special Account not created due to: #{msg}")
  end
end

a帐户在调用第三方API或验证过程中发生任何错误时被销毁。

我想知道如何记录错误以了解为何回滚该事务。

2 个答案:

答案 0 :(得分:0)

ActiveRecord不会在更新失败和/或验证失败时引发Rollback

您应该挽救筹集的资金,然后有两个机会:筹集Rollback仅回滚事务并继续正常的执行流程,或者重新筹集的资金确实破坏了正常的流程。

def special_account_creation
  Account.transaction do
    a = Account.create(params)
    begin
      resp = update_third_party(a) # Throws exception if unable to update
      validate_amount(a, resp)     # Throws exception if `a` is invalid
    rescue => e # might be more specific    
      Rails.logger.info("Transaction failed with a message #{e.message}")
      raise ActiveRecord::Rollback, e.message # just to rollback
      # raise e # for re-raising the exception
    end
  end
end

答案 1 :(得分:0)

ActiveRecord事务捕获内部引发的任何异常并回滚该事务。之后,如果异常为ActiveRecord::Rollback,则该异常被抑制,否则在调用堆栈上方引发任何其他异常。

参考:https://api.rubyonrails.org/classes/ActiveRecord/Rollback.html

因此您可以执行以下操作:

Account.transaction do
  begin
    a = Account.create(params)
    resp = update_third_party(a) # Throws exception if unable to update
    validate_amount(a, resp)     # Throws exception if `a` is invalid
  rescue StandardError => e # <- assuming all errors need to be logged
    Rails.logger.info("Special Account not created due to: #{msg}")
    raise e # <-- Don't forget to raise the error!
  end
end

我建议从救援块中引发错误-这将回滚事务,并且如果错误不是ActiveRecord::Rollback之外的其他错误,它将在事务块之外引发。您可以在基本控制器的rescue_from block中处理这些错误。

也更喜欢捕获StandardError而不是Exceptionhttps://robots.thoughtbot.com/rescue-standarderror-not-exception

我假设我们想捕获该块中所有可能的错误,因此使用StandardError,否则使用需要拦截的任何特定错误。