2017-07-31 10 views
1

複数の並行タスクがあり、すべてがレコード存在をチェックしようとしています。並行タスクの処理

残念ながら、私はすべてのタスクが同時にレコードが存在しないと判断してから、すべてが挿入されると判断しているように、レコードをDBに書き込むことになります。

目的の動作は、挿入を1回だけ取得してから、他のタスクが挿入されたばかりのレコードの存在を認識することです。すべてのヘルプは高く評価され

alias MyApp.Parent, as: Parent 
alias MyApp.Repo, as: Repo 
changeset = Parent.changeset(%Parent{}, model) 

case Repo.all(from p in Parent, where: p.mobile_number == ^model.mobile_number) do 

    [] -> 
    #does not exist 
     case Repo.insert_or_update(changeset) do 
     {:ok, %MyApp.Parent{ id: parent_id }} -> parent_id 
     error_message -> nil 
     end 


    [parent_get_by|t] -> 
    #already exist 
    %MyApp.Parent{ id: parent_id }= parent_get_by 
     parent_id 

end 

は、ここに私の試みです!

答えて

2

mobile_numberフィールドのデータベースにUNIQUE INDEXを追加する必要があります。より効率的です(データベースに1つのクエリを実行するだけで済みます)、テーブルのそのフィールドに重複した値をデータベースに持たせることは絶対にできません。

あなたは3つのことを行う必要があります:

  1. 移行を使用してテーブルにunique_indexを追加します。

  2. チェンジセット機能でunique_constraintに電話をかける。

  3. コントローラでは、ちょうどRepo.insert(changeset)を実行します。フィールドが重複している場合は、エラーメッセージchangeset.errors{:error, changeset}が返されます。

+0

ですから、エラーが発生した場合、私は 'mobile_number'を使って読み込みますか? – simo

+0

既存のレコードを取得したい場合は、後で 'Repo.get_by(Parent、mobile_number:model.mobile_number)'を実行することができます。 – Dogbert

+0

'insert_or_update'の使用はどうですか?既存のレコードに 'get_by'を使用するよりも、レコードが存在していればそれが更新されます。 – simo

0

受け入れられる回答は問題ありません。

ただし、一意のインデックスを強制できない場合は、2つの選択肢があります。最初にロックを使用して、プロセスがdbを同時に照会しないようにします。その例については、elixir_lockerを参照してください。

もう1つの方法は、要求をシリアル化することです。これは、SELECT+INSERTを実行するプロセス、好ましくはGenServerがあることを意味します。それから、db自身に問い合わせるのではなく、プロセスにメッセージを送ります。これにより、要求が相互に実行され、最初の要求だけが挿入され、他の要求は読み込まれます。もちろん、多くのリクエストがある場合は、このプロセスがボトルネックになることがあります。

関連する問題