【问题标题】:ActiveRecord update model while rollback anotherActiveRecord 更新模型同时回滚另一个
【发布时间】:2016-08-22 10:56:24
【问题描述】:

让我们为这个问题提供一些背景信息。给定一个 Ruby on Rails 中的电子商务应用程序。让我们以 2 个模型为例。用户和信用卡。

我的用户在注册后在系统中没有问题。 CreditCard 是带有信用卡信息的模型(是的,我知道 PCI 合规性,但这不是重点)

在信用卡模型中,我包含一个回调 after_validation,它将针对您的银行验证信用卡。

让我在这里放一些简单的代码。

models/user.rb

class User < ActiveRecord::Base
  enum :status, [:active, :banned]
  has_one :credit_card
end

models/credit_card.rb

class CreditCard < ActiveRecord::Base
  belongs_to :user

  after_validation :validate_at_bank

  def validate_at_bank
    result = Bank.validate(info) #using active_merchant by exemple
    unless result.success
      errors.add {credit_card: "Bank doesn't validate"}
      user.banned!
    end
  end
end

controllers/credit_cards_controller.rb

class CreditCardsController < ApplicationController
  def create
    @credit_card = CreditCard.new(credit_card_params) # from Strong Parameters
    if @credit_card.save
      render #success
    else
      render #failure
    end
  end
end

是什么导致了我的问题 看起来 Rails 在我做一个新事务时在 ActiveRecord 中打开了一个事务。此时没有任何内容发送到数据库。

当银行拒绝信用卡时,我想禁止用户。我通过调用禁止来做到这一点!现在我意识到这个更新是针对同一个事务的。我可以看到更新,但是一旦保存不去,一切都会从两个模型中回滚。信用卡没有保存(很好),用户没有保存(这个不好,我要封他)

我尝试添加一个事务包装器,但这只会添加一个数据库检查点。我可以为禁令创造一个延迟的工作,但在我看来,这似乎是矫枉过正。我可以使用 after_rollback 回调,但我不确定这是正确的方法。我有点惊讶,我以前从未遇到过这种情况,这让我相信我的模式不正确,或者我做出这个调用的点不正确。

【问题讨论】:

  • 好吧,我一定是在某个地方搞砸了,因为我无法在侧面项目上复制它。无论如何我都会调查并至少发布这个问题的解决方案。
  • Ok 控制器中的@credit_card.save 开始交易。这很好,这是有道理的。现在,如果我想在交易开始时调用 user.update_column(info: "Not well") 我该怎么做?
  • 找到了一些关于并行处理的资料。 stackoverflow.com/a/20743433/552443 您可以通过启动一个线程并从连接池重新连接数据库来完成此操作。我确定这是可行的,但我发现这种简单的表格更新有点过头了。希望有人能提出一些想法。

标签: ruby-on-rails activerecord model transactions


【解决方案1】:

经过大量审查和更多挖掘,我想出了 3 种方法来处理这种情况。根据您的需求,其中之一应该适合您。

  1. 单独的线程和新的数据库连接
  2. 在保存前显式调用验证函数
  3. 将要执行的任务发送到延迟作业

    • 单独的线程

    以下答案显示了如何执行此操作。

https://stackoverflow.com/a/20743433/552443

这可行,但又不是那么好和简单。

  • 通话有效吗?保存前

这是一个非常快速的解决方案。问题是新开发人员可以删除有效的?线认为 .save 会正确地完成工作。

  • 调用延迟的作业

这可以是任何 ActionJob 提供程序。您可以在单独的线程中发送任务以禁止用户。根据您的设置,这很干净,但不是每个人都需要 DelayedJob。

如果您看到任何内容,请将其添加到 cmets 的新解决方案中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 2016-10-18
    相关资源
    最近更新 更多