2016-04-27 18 views
0

この質問には少しの文脈を置きます。 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コールバックを使用することができますが、これが正しい方法であるかどうかはわかりません。私はちょっと驚いています。前にこのシナリオを捉えたことはありません。自分の父親が正しくない、あるいは私がこの電話をする場所が間違っていると信じています。

+0

私はサイドプロジェクションでこれを再現できないので、どこかでひどくねじれているはずです。私はとにかく調査し、少なくともこの問題の解決策を投稿します。 – Hugues

+0

Okコントローラ内の@ credit_card.saveがトランザクションを開始します。これは問題なく、これは理にかなっています。今、私はuser.update_column(info: "Not well")をトランザクションの開始点から呼びたいと思ったらどうしますか? – Hugues

+0

並列処理に関する情報が見つかりました。 http://stackoverflow.com/a/20743433/552443これを行うには、スレッドを開始し、接続プールからデータベースを再接続します。私はこれが働いていると確信していますが、私はテーブルの簡単な更新のためにこの過度の犠牲を見つけ出すことができます。うまくいけば、誰かがテーブルにいくらかのアイデアをもたらすだろう。 – Hugues

答えて

0

多くのレビューと掘り下げの後、私はこの状況に対処する3つの方法を思いついた。あなたのニーズに応じて、そのうちの1つがあなたのために良いはずです。遅れ仕事

  • 別のスレッド
に実行するタスクを送信明示的
  • を保存する前に検証機能を呼び出す

    1. 別のスレッドと新しいデータベース接続
    2. 次の回答は、操作です。

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

    これは動作しますが、再びその素晴らしく、単純ではないでしょう。

    • 有効ですか?保存前に

    これは非常に簡単な解決策です。問題は、新しい開発者が有効な? 。saveが適切に仕事をすると考えている。

    これは、任意のActionJobプロバイダー可能性があり
  • 遅れ仕事を呼び出す

    • 。別のスレッドでユーザーを禁止するタスクを送信できます。あなたの設定によっては、これはきれいですが、誰もがDelayedJobを必要としているわけではありません。

      何かが表示された場合は、コメントの新しい解決策に追加してください。

    関連する問題