問題の概要

DRFサーバーからDjangoアプリサーバーにWebhook POSTリクエストを送り、
Djangoではこれを受けてPostモデルインスタンスを生成する過程で以下のような問題が発生した。

  1. Webhook応答タイムアウト
  2. Postオブジェクト生成後、ManyToManyフィールドであるcategoriestagsの処理に時間がかかる
  3. このためWebhook応答が遅れ、DRFはこれを失敗と見なす

  4. データ整合性の未確保

  5. 後続処理でCeleryタスクを即座に呼び出す場合、tags.add(...)categories.add(...)の作業が完了していない時点で呼び出される
  6. このためCeleryは不完全なデータを処理する問題が発生する

  7. on_commit()後でもCeleryで空のデータを受信

  8. on_commit()を使用してデータの整合性を確保した後、Celeryタスクを予約したが、タスク内部でtags.all()などの照会結果が依然として空のリストとして表示される
  9. これはCeleryが読み取り作業をレプリカDBに送信しており、master → replicaの同期に遅延があったため

解決戦略

  1. Post生成直後に即時応答を返す
  2. Postインスタンスのみを生成し、DRFには即座に202 Accepted応答を返すことでタイムアウトの問題を回避

  3. 後続処理を別スレッドで行う

  4. threading.Thread()を活用して関係処理およびCelery呼び出しをメインフローから分離
  5. Webhook応答が遅くならないように設計

  6. post_process内でトランザクションを保証

  7. transaction.atomic()ManyToManyフィールド処理全体をトランザクションでまとめる
  8. 作業完了後にtransaction.on_commit()でCeleryタスクを予約
  9. これにより関係処理完了後にCeleryが実行されることを保証

  10. Celery内ではmaster DBを直接指定して照会

  11. レプリカの遅延による整合性問題を防ぐため、Celeryタスク内でusing('default')を明示的に使用
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()

またはDB Routerを活用してCeleryリクエストが常にmaster DBを照会するように分岐可能


最終構造の要約

  1. DRFサーバー → Django Webhookリクエスト
  2. Django:
    • Postオブジェクト生成後即時応答を返す
    • 後続作業は別スレッドで実行
  3. スレッド内:
    • atomic()で関係整理
    • on_commit()でCeleryタスク予約
  4. Celery:
    • master DB(default)からデータ照会

結論

すべての構成要素が個別には正常だが、
分散アーキテクチャ環境で発生するレイテンシとデータ整合性問題を解決するために
コード構造だけでなく、データフローとタイミング、DBレプリカの遅延まで考慮したシステム設計が必要であった。