この質問には少しの文脈を置きます。 Ruby on Railsにeコマースアプリケーションがあるとします。たとえば、2つのモデルを扱いましょう。ユーザーとクレジットカード。ロールバック中のActiveRecord更新モデル
私のユーザーは登録後にシステムに問題はありません。 CreditCardはクレジットカード情報のあるモデルです(PCIコンプライアンスについては知っていますが、それはここでは言及しません)。
クレジットカードモデルでは、クレジットカードの検証バンク。
私はここにいくつかの簡単なコードを入れてみましょう。
モデル/ user.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
コントローラ/ 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コールバックを使用することができますが、これが正しい方法であるかどうかはわかりません。私はちょっと驚いています。前にこのシナリオを捉えたことはありません。自分の父親が正しくない、あるいは私がこの電話をする場所が間違っていると信じています。
私はサイドプロジェクションでこれを再現できないので、どこかでひどくねじれているはずです。私はとにかく調査し、少なくともこの問題の解決策を投稿します。 – Hugues
Okコントローラ内の@ credit_card.saveがトランザクションを開始します。これは問題なく、これは理にかなっています。今、私はuser.update_column(info: "Not well")をトランザクションの開始点から呼びたいと思ったらどうしますか? – Hugues
並列処理に関する情報が見つかりました。 http://stackoverflow.com/a/20743433/552443これを行うには、スレッドを開始し、接続プールからデータベースを再接続します。私はこれが働いていると確信していますが、私はテーブルの簡単な更新のためにこの過度の犠牲を見つけ出すことができます。うまくいけば、誰かがテーブルにいくらかのアイデアをもたらすだろう。 – Hugues