2012-07-03 19 views
7

で期待通りに動作しないロック:データベースは、私は、レールモデルに次のコードを持っているレール&Postgresの

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 

目標が作成されているタイプのバーが二度作成されていないことを確認することです。

コンソールでwith_lockが動作することは、私の期待を裏付けるものです。しかし、プロダクションでは、ロックの一部または全部が期待どおりに動作しておらず、冗長バーが試行されているように見えるため、with_lockは(常に)コードのターンを待たない。

ここで何が起こっている可能性がありますか?

更新 "ロックfooはあなたを助けません"と言っていた人に申し訳ありません!私の例では最初にバーのルックアップがありませんでした。これは今修正されました。

+0

バーは既に存在していますか? –

+1

@FrederickCheung:どうしてあなたは点検してもらえませんか?データベース外のチェックには常に**ホールと競争条件があります。 –

+0

私は個人的にはそうではありませんが、理解のどの部分に欠陥があるかをOPが理解するのに役立つかもしれません。 –

答えて

1

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

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

The issue has been reported on Github。

2

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

+0

私はuniquness制約があり、その仕事をしています。並行保存の1つが失敗することがあります。したがって、冗長データは作成されませんが、コードはそのターンを待たずに爆発します。 –

+0

エラー処理のためにテーブルロックは必要ありません。アプリケーションが非常に遅くなります。 RoRはもっと良いものを持っていなければなりませんが、私はそこでお手伝いすることはできません。 –

+3

@ JohnBachir:ユニークな制約に違反したときに発生する例外をキャッチして処理していないため、爆発します。 **これらの例外をトラップして処理する必要があります。それらを防ぐためにRailsでできることは何もありません。これらの種類の唯一の単純な戦略は、データベースにそれを扱わせることです。あなたのコードは、それを試して、予想される例外や失敗に対処するだけです。 –

6

あなたは何をwith_lockがしているのか混乱しています。 fine manualから:

with_lock(ロック=真)

生じる前に、オブジェクトをロックし、トランザクションに渡されたブロックをラップします。引数としてSQLロック句を渡すことができます(lock!参照)。あなたはwith_lockが内部で何をチェックすれば

、あなたはそれがlock!薄いラッパより少しであることがわかります!

ロック(ロック=真)

入手しますこのレコードの行ロック。要求されたロックを取得するためにレコードをリロードします。

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

このすべてのナンセンスを気にしないでください。このような状況を処理するうえでの唯一の方法は、データベース内でユニークな制約を使用することです。ただし、テーブル全体をロックするなどの不条理なことをしない限り、データベースは一意性を保証できません。次に盲目的にINSERTまたはUPDATEを試して、ユニーク制約に違反した場合に発生する例外を無視してください。

+0

私は 'foo'のロックを使っているのがちょっとハックだったことを知っています。一意性制約のエラーを処理するためのあなたの提案はうまくいっています(実際に何をしてしまったのか) - でも私はまだ知りたいですなぜこのハッキーなコードが機能していないのですか?それは私が望んでいる動作を持っています。これは私がコンソールで確認し、開発環境でテストしたものです。 'foo'のロックを待つ間、あるプロセスは他のプロセスを待つでしょう。私はこのコードからタイムアウトやデッドロックのエラーが発生したことは一度もありませんでした。これは誤った設計の恐れがある副作用です。 –

+0

@JohnBachir: 'foo.with_lock'はどういうことをしているのですか?それがあなたの問題を解決すると思われる理由は何ですか? –

+1

@muistooshort私は、 "このナンセンスをすべて抱きしめてはいけない"という声明にもっと同意することはできません。あなたの提案は私にとって完璧に機能します。ありがとうございました。 – Hoa

1

このような状況を処理するための正しい方法は右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」は、その実際に検索アトミックではありませんし、次に作成しますので、交換してください。このページのドキュメントでは、このケースを正確に説明しています)。

関連する問題