2012-03-28 12 views
6

データベース内のさまざまなテーブルに重複したレコードが多数作成されていることに気づいていますが、なぜこのようなことが起こっているのかが分かりません。興味深いことに、レコードが別の方法で複製されていても(created_atのスタンプまで!)、私たちのusersテーブルでは、パスワードの塩とハッシュは各レコードごとに異なります - 何故かRailsは何らかの形でトランザクション/セーブ操作二度。当然のことながら、アプリケーションコードでsaveまたはcreateを複数回呼び出すことはありません。Railsによって重複レコードが作成される原因は何ですか?

この重複は、データベースに保存されたすべてのレコードでは発生しないように見えますが、まだパターンを推測することはできません。 Userモデルにはvalidates_uniqueness_ofのバリデーションもあります(ただし、テーブルの一意のキーではありませんが、これを行うにはすべての複製をクリーンアップする必要があります)。リクエストが同時に発生している場合は競合状態です。

私たちは現在、アプリケーションサーバー(現在は2台)のPassenger 3.0.11/nginxの背後にあるRails 3.2.2を実行しており、アップストリームの要求をアプリケーションサーバーに送信する1つの中央nginx Webサーバーを持っています。この設定によって何らかの形でプロセスが重複したり、何かが発生する可能性はありますか? 1つのアップストリームサーバーに要求がロックされていないことが重要ですか(つまり、1人のユーザーがイメージなどの静的コンテンツを含むページを要求した場合、1つまたは両方のアプリケーションサーバーが使用される可能性があります)。 (私はストローで握っているように感じますが、すべての可能性をカバーしたい)

これ以外に何が起こる可能性がありますか?

更新:例として、ユーザーが重複したレコードを持って今日を作成しました。両方ともcreated_atのスタンプ2012-03-28 16:48:11を持ち、hashed_passwordsaltを除くすべてのコラムは同一です。だから、作成

1.2.3.4 - - [28/Mar/2012:12:48:10 -0400] "POST /en/apply/create_user HTTP/1.1" 499 0 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-" 
1.2.3.4 - - [28/Mar/2012:12:48:11 -0400] "POST /en/apply/create_user HTTP/1.1" 302 147 "en/apply/create_user" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" "-" 

アプリケーションサーバー1:

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:47:19 -0400 
[2012-03-28 12:47:19] INFO : Processing by ApplyController#create_user as HTML 
[2012-03-28 12:47:20] INFO : Rendered apply/new_user.html.erb within layouts/template (192.8ms) 

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400 
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML 
[2012-03-28 12:48:11] INFO : Redirected to apply/initialize_job_application/3517 
[2012-03-28 12:48:11] INFO : /app/controllers/apply_controller.rb:263:in `block (2 levels) in create_user' 

アプリケーションサーバー2:

Started POST "/en/apply/create_user" for 1.2.3.4 at 2012-03-28 12:48:10 -0400 
[2012-03-28 12:48:10] INFO : Processing by ApplyController#create_user as HTML 

Webサーバーリクエストログから、私は以下を参照してくださいすることができますアクションは3回(おそらくエラーのために最初にフォームに戻る)、各サーバーで少なくとも1回ヒットしました。後者の2つは両方ともウェブサーバによって別々のリクエストとして登録されますが、最初はステータスコード499 Client Closed Request(wikiページによるnginx拡張)を取得し、2番目のものは期待通りに302になります。 499はここで問題を引き起こしていますか?

+0

アプリケーションのログファイルを見たことがありますか?dbに重複するユーザーが存在する場合、ポイントか1つだけか? –

+0

本番環境では、リクエストログだけではなく、mysql bin-log内に完全なレールクエリログがありませんが、作成アクションはリクエストログに複数回表示されています少なくとも1つの実例le(ただし、複数の*重複した*レコードを生成する可能性はありません) –

+0

しかし、重複したレコードまたは単一の要求に一致する要求のペアを確認できますか? –

答えて

5

2つの可能性が考えられます。

ロードバランサとして使用される場合、最初のものはNginxの奇妙な(およびRFCに対して)動作です。次のバックエンドに対する失敗した要求を再試行します。このRFCでは、安​​全な方法(例:GETまたはHEAD)のみが許可されています。この結果、何らかの理由でリクエストが失敗したとnginxが判断した場合、次のサーバに再送信される可能性があります。両方のサーバーがトランザクションを完了しても、レコードは重複しています。 Webサーバーのログ(Nginxがユーザーがブラウザで中止をクリックしたことを示すために使用する499ステータスコード)から判断すると、これは最も考えられる原因のようです。

2番目の方法は、ユーザーが[送信]ボタンをダブルクリックすることです。適切なタイミングで、ブラウザはほぼ同時に2つの完全な要求を送信することができます。

ユーザーレコードが本当に一意であることを確認するには、データベースに一意のインデックスを作成する必要があります。このため、実際にはActiveRecordチェックと比較して悪いエラーメッセージが表示されますが、データベーススキーマとモデルの両方で一意制約を常に定義する必要があります。

また、フロントエンドのnginxに準拠したロードバランサを追加しました。haproxyをお薦めします。

+0

ユーザが要求を打ち切る前に要求がすでにWebサーバからアプリケーションサーバにアップストリームされている可能性はありますか?ウェブサーバーnginxはその要求を '499'とマークしていますか?その場合、ウェブサーバが終了したことを知っているにもかかわらず、リクエストはアプリケーションサーバに存続しますか? –

+0

そうだと思います。要求がRackに送信されると、それはもはや中止されません。だからあなたのapp-server-nginxが接続を中断したことを検知しても、あなたのレールスタックはそれを知らず、何もできません。 nginxが答えを破棄している間、トランザクションは完了します。あなたのフロントエンドロードバランサがすでに間違ってリクエストを再配送している間。 –

+0

も参照してくださいhttp://www.ruby-forum.com/topic/1674379 –

0

本当に競合状態のようです。要求間を確実にロックしてください。 1つまたは2つの要求が今すぐ複製されることは簡単に起こり得ます。取引なしで商品を交換する場合も同じことが起こる可能性がありますので、ご要望の間に競争がないことをご確認ください。

+0

ロックは何ですか?私は(私が間違っていると私には間違っていると思いますが)RailsやMySQLがトランザクションで使用されているテーブルをロックしていると思います。以前はテーブルが長すぎるためにロックされていました。 –

+0

はい、私はトランザクションについて話していません。私はサーバーに送信される要求について話しています。要求マネージャが外部サーバーに要求を正しく割り当てていない場合、競合状態が発生してエントリが複製される可能性があります。それはあなたがマネージャーをどのようにセットアップしたかによって異なりますが、私はそう思う可能性が高いです。 – Spyros

関連する問題