让我们来谈谈这个问题的上下文。给出了RubyonRails中的一个电子商务应用程序。让我们以2个模型为例。用户和信用卡。
我的用户在系统中注册后没有问题。信用卡是一个有信用卡信息的模型(是的,我知道PCI合规性,但这不是重点)
在信用卡模型中,我包含了一个回调after_validation,它将根据您的银行对信用卡进行验证。
让我在这里放一些简单的代码。
型号/用户.rb
class User < ActiveRecord::Base
enum :status, [:active, :banned]
has_one :credit_card
end
型号/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回调,但我不确定这是正确的方式。我有点惊讶,我以前从未发现过这种情况,这让我相信我的父母不正确,或者我打这个电话的地方不正确。
经过多次回顾和深入研究,我想出了3种方法来处理这种情况。根据你的需求,其中一个应该对你有好处。
- 独立的线程和新的数据库连接
- 显式保存之前调用validate函数
-
将要执行的任务发送到延迟作业
- 分离线程
以下答案显示了如何执行此操作。
https://stackoverflow.com/a/20743433/552443
这会起作用,但也不是那么好和简单。
- 呼叫有效吗?保存前
这是一个非常快速的解决方案。问题是,一个新的开发人员可能会删除有效的?认为.save可以正常工作的行。
- 呼叫延迟作业
这可以是任何ActionJob提供程序。您可以在单独的线程中将任务发送给被禁止的用户。根据您的设置,这是非常干净的,但不是每个人都需要DelayedJob。
如果您看到任何内容,请将其添加到评论的新解决方案中。