2017-04-21 17 views
3

いくつかの方法があります。first_or_create_byfind_or_create_byなどの原則に関する作業:データベースへActiveRecordのfind_or_create *メソッドに根本的に欠陥がありますか?

  1. 話それを作る、我々はそれを見つけなかった場合、我々は
  2. が欲しいものを見つけることを試みるためには、自分自身は
  3. 明らかに、これらのメソッドの同時呼び出しは、彼らが望むものを見つけることができません両方のスレッドを持つことができデシベル

に保存して、ステップ3 1で予期せずに失敗します。

  1. は、事前にごDBに賢明な一意性制約を作成:よりよい解決策があるよう

    は、ある create_or_find

    です。

  2. 保存したい場合は保存してください
  3. うまくいきましたら、保存してください。
  4. それが原因RecordNotUnique例外の動作しなかった場合、それはすでにそこだ、偉大な、一見(だから私はRailsの組み込みのものを使用したいどのような状況で、私自身ではない、それを

をロードより信頼性の高い)create_or_find

+2

説明していることは、多くのデータベースでサポートされているアップサルトです。 – Anthony

+0

@Anthony権利、実際に私たちが使いたい宝石、https://github.com/zdennis/activerecord-importは、それをサポートしています。しかし、私はなぜデフォルトのルビーのものがそのように設計されるのかはっきりしないのですか?より多くの場合失敗する傾向があるようです。 – z5h

答えて

5

掘り起こした後、自分の質問に答えるつもりです。

検索または作成するための文書がによって言う:

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

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

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

これは一般に、create_or_findよりも優れたパフォーマンスを発揮します。

成功した場合、create_or_findには1回のDB旅行が必要であると考えてください。これは一意のレコードにつき1回のみ発生します。毎回2回のDBトリップが必要です(作成と検索に失敗しました)。

再試行されたfind_or_createは、失敗(検索、失敗した作成、検索)の場合は3回のトリップが必要ですが、非常に小さなウィンドウで何度も発生する可能性があります。それを超えると、すべての他のコールはfind_or_createレコードになり、DBの1回のトリップが必要になります。

したがって、再試行の償却されたコストは、より良いと迅速に到達しました。

+1

私は実際にActiveRecordにパッチを当てているのですが、デフォルトの 'find_or_create *'メソッドはかなり不慣れなIMOです。 –

+0

@muistooshort rails coreに修正を提案することを検討していますか? – katzmopolitan

0

明らかにスレッドセーフではありませんが、パフォーマンスが向上するように設計されている可能性があります。

最初に見つけて、必要に応じて作成するほうがずっと時間がかかり、ほとんどの場合、例外を回避することができます(処理できます)。

こちらのdiscussionはお役に立ちます。

関連する問題