Обзор проблемы
Сервер DRF отправляет POST запрос вебхука на сервер приложения Django,
в процессе которого возникает следующая проблема при создании экземпляра модели Post.
- Тайм-аут ответа вебхука
- После создания объекта Post требуется время для обработки полей
ManyToMany
, таких какcategories
иtags
-
Из-за этого ответ вебхука задерживается, и DRF интерпретирует это как ошибку
-
Недостаточная целостность данных
- При немедленном вызове задачи Celery после обработки возникает проблема, когда выполняются команды
tags.add(...)
,categories.add(...)
до завершения этих операций -
В результате Celery обрабатывает неполные данные
-
Получение пустых данных в Celery даже после on_commit()
- После обеспечения целостности данных с помощью
on_commit()
задача Celery назначается, но результаты запросов внутри задачи, такие какtags.all()
, по-прежнему возвращают пустой список - Это произошло потому, что Celery отправлял запросы на чтение в реплику БД, и существовали задержки в синхронизации master → replica
Стратегия решения
- Возврат ответа сразу после создания Post
-
Создается только экземпляр Post, и сразу же возвращается ответ
202 Accepted
, что позволяет избежать проблемы с тайм-аутом -
Обработка последующих задач в отдельном потоке
- Использовать
threading.Thread()
, чтобы разделить обработку отношений и вызов Celery от основного потока -
Разработать так, чтобы ответ вебхука не замедлялся
-
Гарантировать транзакцию внутри post_process
- Объединить обработку полей
ManyToMany
в транзакцию с помощьюtransaction.atomic()
- После завершения операций назначить задачу Celery с помощью
transaction.on_commit()
-
Это гарантирует выполнение Celery после завершения обработки взаимосвязей
-
Указать master DB для чтения внутри Celery
- Чтобы избежать проблем с целостностью из-за задержки реплики, явно использовать
using('default')
внутри задачи Celery
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()
Или использовать маршрутизатор БД для того, чтобы запросы Celery всегда обращались к master DB
Итоговая структура
- Сервер DRF → Запрос вебхука Django
- Django:
- Немедленный возврат ответа после создания объекта Post
- Последующие задачи выполняются в отдельном потоке
- Внутри потока:
- Упорядочить отношения с помощью
atomic()
- Назначить задачу Celery с помощью
on_commit()
- Упорядочить отношения с помощью
- Celery:
- Запрос данных из master DB (
default
)
- Запрос данных из master DB (
Заключение
Все компоненты работают нормально по отдельности, но
для решения проблем с задержкой и целостностью данных, возникающих в распределенной архитектуре,
необходима была система проектирования, учитывающая не только структуру кода, но и поток данных, тайминг и задержки реплики БД.
댓글이 없습니다.