問題の概要
DRFサーバーからDjangoアプリサーバーにWebhook POSTリクエストを送り、
Djangoではこれを受けてPostモデルインスタンスを生成する過程で以下のような問題が発生した。
- Webhook応答タイムアウト
- Postオブジェクト生成後、
ManyToMany
フィールドであるcategories
、tags
の処理に時間がかかる -
このためWebhook応答が遅れ、DRFはこれを失敗と見なす
-
データ整合性の未確保
- 後続処理でCeleryタスクを即座に呼び出す場合、
tags.add(...)
、categories.add(...)
の作業が完了していない時点で呼び出される -
このためCeleryは不完全なデータを処理する問題が発生する
-
on_commit()後でもCeleryで空のデータを受信
on_commit()
を使用してデータの整合性を確保した後、Celeryタスクを予約したが、タスク内部でtags.all()
などの照会結果が依然として空のリストとして表示される- これはCeleryが読み取り作業をレプリカDBに送信しており、master → replicaの同期に遅延があったため
解決戦略
- Post生成直後に即時応答を返す
-
Postインスタンスのみを生成し、DRFには即座に
202 Accepted
応答を返すことでタイムアウトの問題を回避 -
後続処理を別スレッドで行う
threading.Thread()
を活用して関係処理およびCelery呼び出しをメインフローから分離-
Webhook応答が遅くならないように設計
-
post_process内でトランザクションを保証
transaction.atomic()
でManyToMany
フィールド処理全体をトランザクションでまとめる- 作業完了後に
transaction.on_commit()
でCeleryタスクを予約 -
これにより関係処理完了後にCeleryが実行されることを保証
-
Celery内ではmaster DBを直接指定して照会
- レプリカの遅延による整合性問題を防ぐため、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を照会するように分岐可能
最終構造の要約
- DRFサーバー → Django Webhookリクエスト
- Django:
- Postオブジェクト生成後即時応答を返す
- 後続作業は別スレッドで実行
- スレッド内:
atomic()
で関係整理on_commit()
でCeleryタスク予約
- Celery:
- master DB(
default
)からデータ照会
- master DB(
結論
すべての構成要素が個別には正常だが、
分散アーキテクチャ環境で発生するレイテンシとデータ整合性問題を解決するために
コード構造だけでなく、データフローとタイミング、DBレプリカの遅延まで考慮したシステム設計が必要であった。
Add a New Comment