¿La tarea Django con subprocesos no maneja automáticamente las transacciones o las conexiones de base de datos?

Tengo a Django configurado para ejecutar algunas tareas recurrentes en sus propios subprocesos, y me di cuenta de que siempre estaban dejando procesos de conexión de base de datos sin terminar (pgsql “Idle In Transaction”).

Revisé los registros de Postgres y descubrí que las transacciones no se estaban completando (sin ROLLBACK). Intenté usar los distintos decoradores de transacciones en mis funciones, sin suerte.

Cambié a la gestión manual de transacciones e hice la reversión manualmente, funcionó, pero aún así dejé los procesos como “Inactivo”.

Entonces llamé a connection.close (), y todo está bien.

Pero me pregunto, ¿por qué la administración de transacciones y conexiones típica de Django no funciona para estas tareas de subprocesos que se generan a partir del subproceso de Django principal?

Después de semanas de probar y leer el código fuente de Django, encontré la respuesta a mi propia pregunta:

Actas

El comportamiento predeterminado de confirmación automática de Django sigue siendo válido para mi función de subprocesos. Sin embargo, se establece en los documentos de Django:

Tan pronto como realiza una acción que necesita escribir en la base de datos, Django produce las declaraciones INSERT / UPDATE / DELETE y luego realiza el COMMIT. No hay ROLLBACK implícito.

Esa última oración es muy literal. NO emite un comando ROLLBACK a menos que algo en Django haya puesto la bandera sucia. Como mi función solo estaba haciendo sentencias SELECT, nunca activó el indicador sucio y no activó un COMMIT.

Esto va en contra del hecho de que PostgreSQL piensa que la transacción requiere un ROLLBACK porque Django emitió un comando SET para la zona horaria. Al revisar los registros, me lancé porque seguí viendo estas declaraciones ROLLBACK y asumí que la administración de transacciones de Django era la fuente. Resulta que no, y eso está bien.

Conexiones

La gestión de la conexión es donde las cosas se ponen difíciles. Resulta que Django usa signals.request_finished.connect(close_connection) para cerrar la conexión de base de datos que normalmente usa. Como normalmente no ocurre nada en Django que no implique una solicitud, tomas este comportamiento por sentado.

En mi caso, sin embargo, no hubo solicitud porque el trabajo estaba progtwigdo. Ninguna solicitud significa que no hay señal. Ninguna señal significa que la conexión de la base de datos nunca se cerró.

Volviendo a las transacciones, resulta que simplemente emitir una llamada a connection.close() en ausencia de cualquier cambio en la gestión de transacciones emite la instrucción ROLLBACK en el registro de PostgreSQL que estaba buscando.

Solución

La solución es permitir que la administración normal de transacciones de Django proceda de la forma habitual y simplemente cerrar la conexión de una de estas tres formas:

  1. Escriba un decorador que cierre la conexión y envuelva las funciones necesarias en él.
  2. Enganche a las señales de solicitud existentes para que Django cierre la conexión.
  3. Cierre la conexión manualmente al final de la función.

Cualquiera de esos tres funcionará (y funcionará).

Esto me ha vuelto loco por semanas. Espero que esto ayude a alguien más en el futuro!