18

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

first_or_createRails ActiveRecordは競合状態を防ぎますか?

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

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

複数のプロセスとの同期の問題が原因で、エントリが重複してデータベースが作成される可能性はありますか?

4

3 に答える 3

26

Rails 4のドキュメントにfind_or_create_byは、この状況に役立つ可能性のあるヒントが記載されています。

このメソッドはアトミックではなく、最初にSELECTを実行し、結果がない場合はINSERTが試行されることに注意してください。他のスレッドまたはプロセスがある場合は、両方の呼び出しの間に競合状態があり、2つの同様のレコードが発生する可能性があります。

それが問題であるかどうかはアプリケーションのロジックに依存しますが、行にUNIQUE制約がある特定のケースでは、例外が発生する可能性があります。再試行してください。

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

同様のエラーキャッチがRails3で役立つ場合があります(Rails 3で同じActiveRecord::RecordNotUniqueエラーがスローされるかどうかわからないため、実装を変える必要がある場合があります)。

于 2013-11-01T12:23:10.063 に答える
5

はい、可能です。

楽観的または悲観的なロックとの競合の可能性を大幅に減らすことができます。もちろん、楽観的ロックではテーブルにフィールドを追加する必要があり、悲観的ロックも同様に拡張できません。さらに、データストアの機能によって異なります。

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

于 2012-05-17T18:51:22.907 に答える
0

first_or_createがアトミックだとは思わない。コンソールから選択操作が表示され、作成します。したがって、ロックが必要です。パシミスティックロックを使用する場合、追加の列は必要ありません。Advisory_lockを使用します。

SomeModel.with_advisory_lock("get_or_create_#{some_key}") do
    SomeModel.where(external_id: external_id).first_or_create
end
于 2021-05-02T11:07:30.103 に答える