1

このデータベース トランザクションを外部 API 呼び出し (S3 バケットの存在を確認する) と組み合わせる必要があることを除いて、この回答に沿った代替get_or_createメソッドがあるとします。データベース レベル (MySQL InnoDB) で一意の制約があるとします。イベントレットを使用して、複数の Gunicorn ワーカー プロセス内でこのコードを実行しています。

@transaction.commit_on_success
def get_or_create_with_func(obj, func_to_call=None, **kwargs):
    try:
        result, created = obj.objects.create(**kwargs), True
    except IntegrityError:
        transaction.commit()
        result, created = obj.objects.get(**kwargs), False

    func_result = None
    if func_to_call is not None and created:
        func_result = func_to_call()

    return result, created, func_result

トランザクションの成功は、func_to_call(例外を発生させずに) 正常に戻ることが条件です。例外が発生した場合、明らかにトランザクションはロールバックします。

私が確信していないのは、2 つのスレッドが同時にこの関数を使用している場合create、データベースにエントリを保存するか (それIntegrityErrorが発生し、呼び出しがオブジェクトを返す)、終了getするまで「待機」する必要があるかどうかです。func_to_call?

基本的に、上記の関数を次のように呼び出して、このシナリオを想像しています。

query_result, created, func_result = get_or_create_with_func(MyModel, external_api_func, model_column=some_value)
  1. スレッド 1 と 2 の両方get_or_create_with_funcがほぼ同時に呼び出されます。
  2. 両方とも、経由でエントリを作成しようとしますobj.objects.create
  3. スレッド 1 がそれ​​を正常に作成したとしましょう。次に、try/except ブロックを終了してから を実行しfunc_to_callます。
  4. スレッド 2 もオブジェクトの作成を試みます。
  5. 理想的には、スレッド 2 はIntegrityError.
  6. スレッド 1 は の呼び出しfunc_to_callを終了しますが、例外は発生しません。
  7. スレッド 2 は「コミット」する (スレッド 1 も既にコミットを終了している) ため、 を呼び出してget、スレッド 1 によって書き込まれた DB オブジェクトを期待どおりに取得できます。

また

  1. スレッド 1 が呼び出さfunc_to_call れ、例外が発生します。
  2. スレッド 1 により、django はトランザクションをロールバックするため、DB エントリは存在しません。
  3. IntegrityErrorスレッド2 は引き続きget.

私の質問は、ステップ 4 でcreate、テーブルの一意の制約が原因で失敗するか、それともスレッド 1 が実際にはまだトランザクションをコミットしていないため (func_to_callかなり遅いとしましょう)、成功するでしょうか?

4

0 に答える 0