2012-05-17 15 views
15

私は自分のテーブルフィールドの値の一意性を強制しようとしています。テーブルの変更はオプションではありません。条件付きでテーブルに行を挿入するには、ActiveRecordを使用する必要がありますが、私は同期が心配です。Railsのレース条件first_or_create

Rails ActiveRecordのfirst_or_createは競合状態を防ぎますか?

これはGitHubのからfirst_or_createのソースコードです:

def first_or_create(attributes = nil, options = {}, &block) 
    first || create(attributes, options, &block) 
end 

はそれが重複したエントリが複数のプロセスとの同期の問題のためにデータベースをもたらすことは可能ですか?

+2

ARはこのような競合状態でいっぱいです。 – dbenhur

+0

参照(SO dup)[私のRailsアプリケーションの競合状態を避けるにはどうすればいいですか?](http://stackoverflow.com/questions/3037029/how-do-i-avoid-a-race-condition-in-my -rails-app)とRails Cookbook [オプティミスティックロックによる競合条件の回避](http://underpop.free.fr/r/ruby-on-rails/cookbook/I_0596527314_CHP_3_SECT_19.html) – dbenhur

+0

@dbenhur - 私は使用できませんオプティミスティック・ロックは、表にフィールドを追加するためです。私の条件の1つは、フィールドを重複しないように追加することができないということでした。 –

答えて

5

はい、可能です。

optimisticまたはpessimistic lockingのいずれかとの競合を大幅に軽減できます。もちろん、楽観的なロックではテーブルにフィールドを追加する必要があり、悲観的なロックもスケールされません。また、データストアの機能によって異なります。

追加の保護が必要かどうかはわかりませんが、利用可能です。

+0

テーブルの変更はオプションではなかったので、私はオプティミスティックロックを使用できませんでした。悲観的なロックの問題は、InnoDB固有の構文を挿入しなくても、Railsで必要なロックの分離レベルを指定できなかったことです。重複行の可能性にもかかわらず、 'first_or_create'を使用して終了しました。 ActiveRecordの検証ヘルパーを使用して一意性を確保できたとは思うが、 –

+2

彼らは同じ競合状態に陥っています –

17

レール4 documentation for find_or_create_byこのような状況のために有用であるかもしれない先端を提供する:

に注意してくださいこの方法はアトミックではない、それは最初のSELECTを実行し、結果がない場合INSERTであります試みた。他のスレッドやプロセスがある場合は、両方の呼び出しの間に競合状態が存在し、2つの同様のレコードで終了する可能性があります。それが問題であるか否か

アプリケーションのロジックに依存するが、行は例外が発生することができるUNIQUE制約を有する特定の場合には、単に再試行:

begin 
    CreditAccount.find_or_create_by(user_id: user.id) 
rescue ActiveRecord::RecordNotUnique 
    retry 
end 

同様エラーキャッチはRails 3にとっては便利かもしれません。(同じActiveRecord::RecordNotUniqueエラーがRails 3でスローされたかどうかは分かりませんので、実装が異なる必要があります)

+0

レコードのために:Rails 3は 'ActiveRecord :: StatementInvalid'(非常に具体的ではないもの)をスローします。 – spickermann

+0

Bummer。ですから、Rails 4と同じように読み込まなくても、リトライするために 'rescue ActiveRecord :: StatementInvalid'を実行する可能性がありますか? –

+0

できます。しかし問題は、 'StatementInvalid'には無効なSQLも含まれ、NULLでない列には' NULL'が含まれているということです。あなたは決してうまくいかないことをやり直すかもしれません。おそらく、あなたはループに陥るのを避けるためにhttps://github.com/nfedyashev/retryable#readmeのようなものを使いたいでしょう。 – spickermann

関連する問題