6

Railsアプリケーションに奇妙な問題があります。私のアプリケーションは1秒以内に重複するPOSTリクエストを受け入れます

同じデータを含むこの重複したリクエストは、奇妙なことに私のモデルの一意性の検証をバイパスすることができます。これにより、まったく同じ内容の2行のデータが作成されます。

本当に困惑したのは、昨日から1日1回しか起こらなかったのですが、何が原因なのかわかりません。(システムはすでに稼働しており、クライアントによって使用されています。このメソッド呼び出しは1日に200〜300回使用され、これをまったく再現できません)

これが私のコードスニッペントの状況であり、時系列で完全なコードにリンクしています

  1. ユーザーが新しいトランザクションを作成したい場合、コントローラーでこのメソッドを呼び出します

    def new  
      @penjualan = Penjualan.new  
      @penjualan.kode_transaksi = "J"+ DateTime.now.strftime("%d%m%Y%H%M%S")+@active_user.id.to_s  
      @customers = Customer.all(:limit => cookies[:limit], :order=>:kode_kustomer )  
      @barangs = Barang.all(:limit => cookies[:limit] )  
      respond_to do |format|  
        format.html # new.html.erb  
        format.json { render json: @penjualan }  
      end     
    end  
    

    http://pastebin.com/Lmp7hncn行648の完全なコントローラー

  2. 「新規」ビューで、:disable_withを使用してボタンを無効にしました。これにより、ユーザーは送信ボタンを2回クリックできなくなり、ユーザーが二重のPOSTリクエストを開始できなくなります。

    .row  
      .span4  
        = f.submit 'Proses', :class=>"btn btn-large btn-primary", :disable_with => "Processing..."
    

    http://pastebin.com/7b9W68RY行97の全景

  3. 送信されたリクエストは、#1と同じコントローラーであるコントローラーで「create」メソッドを呼び出します。このメソッドは、1秒の差で2回呼び出されます。さらに奇妙なことに、このリクエストは、モデルで定義した一意性の検証をバイパスします。この場合、最初のリクエストと同じkode_transaksiを持つために2番目のリクエストが失敗することになっています。

  4. モデル(Penjualan)の属性に一意性の制約があります(kode_transaksi)

    class Penjualan < ActiveRecord::Base  
      attr_accessible :customer_id, :jatuh_tempo, :kode_transaksi, :no_sj, :tanggal_bayar, :tanggal_transaksi, :total,:total_diskon, :ongkos, :user_id, :status_pembayaran, :is_returned, :kartu_kredit, :kartu_debit  
      has_many :detil_penjualans  
      attr_accessible :cash_total, :kembali  
      belongs_to :user  
      belongs_to :customer  
    
      validates :kode_transaksi, :uniqueness =>{:message=>"Transaksi Sudah Terjadi"}  
    
      scoped_search :on => [:kode_transaksi, :tanggal_transaksi, :status_pembayaran, :tanggal_bayar, :jatuh_tempo, :total ]  
      scoped_search :in => :customer, :on => [:nama_kustomer, :kode_kustomer]  
      scoped_search :in => :user, :on => [:username]  
    end  
    
  5. ケースのスニペットを含む私の本番ログ

    Started POST "/penjualans" for 192.168.1.104 at 2012-11-24 12:15:40 +0900   
    Processing by PenjualansController#create as HTML     
    Parameters: {.... too long, see below ....}  
    
    
    Started POST "/penjualans" for 192.168.1.104 at 2012-11-24 12:15:41 +0900   
    Processing by PenjualansController#create as HTML     
    Parameters: {..... too long, see below ....}   
    Redirected to url/penjualans/17403   
    Completed 302 Found in 378ms (ActiveRecord: 246.0ms)   
    Redirected to url/penjualans/17404   
    Completed 302 Found in 367ms (ActiveRecord: 233.8ms)
    

ログのスニペットhttp://pastebin.com/3tpua9gi

  1. この状況により、データベースに重複エントリが作成され、問題が発生しました

私はこの振る舞いに本当に困惑していて、私は気が遠くなりました。どんな助けでも大歓迎です。

4

2 に答える 2

6

問題をすばやく修正するには、モデル以外にデータベースに一意の制約を追加することをお勧めします。

Railsのドキュメントでは、2つの接続で同じ一意の値が同時に挿入される問題を防ぐために、一意性の検証にはデータベース内の一意の制約を伴う必要があることが示されています。

それ以外に、ユーザーがフォームをすばやく連続してダブルクリックすることに問題があるのでしょうか。フォームの無効化が正しく機能しないため、ユーザーが2回クリックできるようになっている可能性がありますか?

毎日同じ時間ですか、それとも特定の時間だけですか?

于 2012-11-26T12:18:11.233 に答える
3

この問題は、モデルベースの一意性制約がRailsで実装される方法が原因で発生します。基本的に、それらは、指定された一意性制約に対して既存の行があるかどうかをデータベースに尋ね、その場合はオブジェクトの作成を拒否することによって機能します。

ただし、一般的に使用されるトランザクション分離レベル(通常は繰り返し可能読み取り)を考えると、トランザクションが重複している可能性があります。これらのトランザクションは、制約を正常にチェックし、お互いを知らなくてもオブジェクトを挿入します。

これは、実際の一意性を実現するには、インデックスを使用してデータベースに制約を定義する必要があるためです。UNIQUEマルチスレッド操作中に行が実際に挿入/更新されるときに制約をチェックすることで実際の一意性を保証できるのはデータベースのみであるため、これはモデルで制約を定義するよりもはるかに重要です。

Rubyで制約を追加で定義する必要がある唯一の理由は、エラーメッセージがはるかにわかりやすく、一般的なケースを処理できるためです。

Rails制約ではなくデータベース制約がヒットした場合、失敗したデータベース制約を除いて何がうまくいかなかったかについて多くの情報なしでfalse呼び出すときに戻ってきます。saveただし、利点は、後で一貫性のあるデータベースを維持できることが保証されていることです。

于 2012-11-26T12:34:41.550 に答える