9

Railsモデルに次のコードがあります:

foo = Food.find(...)
foo.with_lock do
  if bar = foo.bars.find_by_stuff(stuff)
    # do something with bar
  else
    bar = foo.bars.create!
    # do something with bar
  end
end

目標は、作成されるタイプのバーが 2 回作成されないようにすることです。

コンソールでの with_lock の動作のテストは、私の予想を裏付けています。ただし、本番環境では、一部またはすべてのケースでロックが期待どおりに機能せず、冗長な Bar が試行されているようです。そのため、with_lock は (常に?) コードが順番を待つことにはなりません。 .

ここで何が起こっているのでしょうか?

「 foo をロックしても役に立たない」と言っていた皆さん、ごめんなさい!! 私の例には、最初はバールックアップがありませんでした。これは修正されました。

4

4 に答える 4

7

あなたは何をするのか混乱してwith_lockいます。細かいマニュアルから:

with_lock(ロック = 真)

渡されたブロックをトランザクションでラップし、譲る前にオブジェクトをロックします。SQL ロック句を引数として渡すことができます ( を参照lock!)。

内部で何with_lockが行われているかを確認すると、それが薄いラッパーにすぎないことがわかりますlock!

ロック!(ロック = 真)

このレコードの行ロックを取得します。レコードをリロードして、要求されたロックを取得します。

したがってwith_lock、単純に行ロックを実行し、 の行をロックしfooます。

このすべてのナンセンスなロックを気にしないでください。この種の状況を処理する唯一の正気な方法は、データベースで一意制約を使用することです。テーブル全体をロックするなどのばかげたことをしたくない限り、データベース以外の誰も一意性を保証できません。次に、やみくもにINSERTまたはUPDATEを試してトラップし、一意の制約に違反したときに発生する例外を無視してください。

于 2012-07-03T17:28:55.123 に答える
3

この状況を処理する正しい方法は、Rails のドキュメントに記載されています。

http://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by

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

(「find_or_create_by」はアトミックではなく、実際には検索してから作成します。したがって、それを検索に置き換えてから作成します。このページのドキュメントは、このケースを正確に説明しています。)

于 2016-07-27T20:14:26.777 に答える
2

一意の制約を使用しないのはなぜですか? ユニークさのために作られています

于 2012-07-03T14:48:07.430 に答える
1

クエリキャッシュのRailsアプリでロックが機能しない理由。

1回のリクエストで同じ行の排他ロックを複数回取得しようとすると、クエリのキャッシュが開始されるため、後続のロッククエリがDB自体に到達することはありません。

この問題はGithubで報告されています。

于 2012-07-05T09:23:31.413 に答える