2017-12-12 14 views
3
私は同時に最大20人ではなく、今のように、複数の人が最初の要素にアクセスすることができ、内部で使用するために起こっているキューをリファクタリング/見直してる

への同時アクセスを避けてください(私たちは、それがローカルにクリックしようとしました同時にリンク)同じキュー要素

フラックスはこれに類似している:。

views.py

[GET] 
def list_operator(request, id): 
    if request.POST: 
    utilitary = Utilitary(id) 
    pool = ThreadPool(processes=1) 
    async_result = pool.apply_async(utilitary.recover_people, (id,)) 
    return_val = async_result.get() 
    person = People.objects.get(pk=return_val) 
    return redirect('people:people_update', person.pk) 

utilitary.py

このファイルのメソッドはrecover_peopleで、複数のテーブルにわたって約4-5のクエリ(人はflag_allocated=False)を実行し、リストをソートして最初の要素を返します。 最後のステップは、このいずれかになります。

for person in people: 
    p_address = People_Address.objects.get(person_id=person.id) 
    p_schedule = Schedules.objects.get(schedules_state=p_address.p_state) 

    if datetime.now() > parse_time(p_schedule.schedules_hour): 
    person = People.objects.get(pk=person.id) 
    person.flag_allocated = True 
    person.date_of_allocation = datetime.now() 
    person.save() 
    return person.pk 

おそらくUtilitaryメソッドのロジックで何かが間違っていますか?あるいは、私はこの量の人々が同時にこの方法を呼び出すことでこの問題を予期しているはずですか?

は、キャッシュのヘルプを使用してもらえますか?申し訳ありません、私はdjangoとmvcを初めて使っています。

+0

間違いなく私には競合状態のようです。彼らはすべて同じクエリを実行し、同じ結果を得、同じ仕事をする可能性が非常に高い。あなたは仕事の各作業単位に労働者を「登録する」方法を考えてみるかもしれません。 –

+0

@DanFarrell申し訳ありませんが、例やリンクを提供できますか?これは私にとってとても新しいものです。 – Onilol

+0

これは実際どのように「キュー」ですか? –

答えて

2

、データベースに「人」を選択するようにたくさんのように思えます再度メモリにロックしてデータベースに書き込むだけです。このアクションはアトミックではありません。

あなたはこれらのアクションの間に、他のプロセスによってロックされたレコード持つことができます。

person = People.objects.get(pk=person.id) 
person.flag_allocated = True 
person.date_of_allocation = datetime.now() 
person.save() 

問題がどこにあるかです。しかし...データベースのレコードを直接更新する場合、更新がレコードに書き込む条件を渡すのは、flag_allocated=Falseです。更新が行に影響するかどうかを確認するだけです。そうでない場合は、キューの次の人に移動します。更新はレコードがallocation_flag(SQL原理)にそれを書くためにロックされています

for person in people: 
    rows = People.objects.filter(pk=person.id, flag_allocated=False).update(flag_allocated=True) 
    if rows: 
     break # Got the person... And nobody else will. 

:よう

何か。 2つの更新が同じ行を混乱させようとすると、最初にそれを行い、次に2番目の更新は何も更新せず、次の人を試すでしょう。

2

あなたは並行プロセス間の干渉を防ぐために何もしていない場合、彼らは遅かれ早かれ、互いのつま先を踏むためにバインドされています。

データベースにキューをモデル化するまでの時間光栄アプローチは、ジョブを実行する前に、トランザクション一貫性のある方法で特定のジョブの作業者を登録することです。

を使用すると、ジョブIDや仕様、最初はnull状態、および労働者のために最初はnull値の列を持つテーブルworkを持っていると言います。労働者は、次のような

Update `work` set worker = my_worker_id, status=initializing where status is null and worker is null limit 1. 

つだけのワーカーが原因「どこ」節に次のジョブを「登録」することができます更新を実行することで、ジョブに「登録」することができます。

これは完璧ではない - あなたはまだ失敗した作業者が孤立したジョブを処理する必要があります。仕事の完了時に更新された状態の列は、労働者のための何らかの心拍と組み合わせられ、仕事の偶発性に関する注意深い設計は、仕事が失敗した労働者やAWOL労働者につかれないことを保証するプリミティブを与えるでしょう。

+0

だから、セロリのようなものが助けになるだろうか? – Onilol

+1

私は間違いなくキューイングフレームワークを追加することをお勧めします。私は過去に自分の仕事でセロリを使用し、それが容認できると判断しました。 –

2

ここでの標準的な解決策は、何らかの種類のロックを使用することであるため、utilitary.recover_peopleの2つの同時実行はできません。この関数は、ロックを取得して実行し、ロックを解放するまで待機します。

通常、Djangoは複数のプロセスによって処理されます(そして、これを変更したくない場合)、ロックを永遠に保つために電話を掛けたくない場合、ここでの良い解決策は、 (すべてのdjangoプロセスがもちろん同じredis dbを共有する)redisのようなものですが、有効期限は妥当な時間に設定され、永遠に設定されません。

セロリを使用した設定の例があります(ここでセロリを必ず必要とするわけではありません。セロリを使ってお互いにステッピングするのを避けるために、原理は同じです)。

この場合、あなたはまた、単にロックを保存するために、あなたのSQLデータベースを使用することができますが、あなたは自動的に有効期限を持っていない...

+0

私はredisをキューイングにも使用しており、リレーショナルデータベースよりも適していることがわかりました。 –

関連する問題