Resumen del Problema
Se envió una solicitud POST de webhook desde el servidor DRF al servidor de aplicaciones Django,
y Django encontró el siguiente problema durante el proceso de creación de una instancia del modelo Post.
- Tiempo de espera de respuesta del webhook superado
- Después de la creación del objeto Post, se requiere tiempo para procesar los campos
ManyToMany
comocategories
ytags
-
Esto provoca un retraso en la respuesta del webhook y DRF lo interpreta como un fracaso
-
No se asegura la integridad de los datos
- Si se llama a la tarea de Celery de inmediato para el procesamiento posterior, se llama en un momento en que las operaciones
tags.add(...)
,categories.add(...)
aún no se han completado -
Esto resulta en que Celery procese datos incompletos
-
Recepción de datos vacíos en Celery incluso después de on_commit()
- Se reservó la tarea de Celery después de asegurar la integridad de los datos con
on_commit()
, pero los resultados de las consultas comotags.all()
todavía mostraron listas vacías - Esto se debe a que Celery estaba enviando las operaciones de lectura a la base de datos réplica, y existía un retraso en la sincronización de master a réplica
Estrategia de Solución
- Devolver la respuesta inmediatamente después de crear el Post
-
Creando solo la instancia de Post y devolviendo inmediatamente una respuesta de
202 Accepted
a DRF para evitar problemas de tiempo de espera -
Procesar trabajos posteriores en un hilo separado
- Utilizando
threading.Thread()
para separar el procesamiento de relaciones y la llamada a Celery del flujo principal -
Diseñado para no hacer que la respuesta del webhook se retrase
-
Asegurar la transacción dentro de post_process
- Envolver todo el procesamiento del campo
ManyToMany
contransaction.atomic()
- Reservar la tarea de Celery con
transaction.on_commit()
después de completar las operaciones -
Esto asegura que Celery se ejecute después de completar el procesamiento de relaciones
-
Especificar directamente la base de datos master para las consultas dentro de Celery
- Para evitar problemas de integridad debido a los retrasos en la réplica, utilizar
using('default')
explícitamente dentro de la tarea de Celery
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()
También se puede utilizar un DB Router para dirigir las solicitudes de Celery a que siempre consulten la base de datos master
Resumen de la Estructura Final
- Servidor DRF → Solicitud de webhook de Django
- Django:
- Devolver respuesta inmediatamente después de crear el objeto Post
- Los trabajos posteriores se ejecutan en un hilo separado
- Dentro del hilo:
- Limpiar relaciones con
atomic()
- Reservar tarea de Celery con
on_commit()
- Limpiar relaciones con
- Celery:
- Consultar datos desde la base de datos master (
default
)
- Consultar datos desde la base de datos master (
Conclusión
Aunque todos los componentes operan normalmente de manera individual,
es necesario un diseño del sistema que considere no solo la estructura del código, sino también el flujo de datos y los tiempos, así como los retrasos de las bases de datos réplicas, para solucionar problemas de latencia e integridad de datos en un entorno arquitectónico distribuido.
Add a New Comment