Django + MySQL: savepoint no existe?

Estoy ejecutando una pequeña aplicación web en un plan de alojamiento compartido. Tengo una “función de trabajo” que contiene un bucle infinito; el bucle comprueba una cola de tareas en la base de datos para cosas nuevas que hacer. Esto requería el uso de @transaction.commit_manually para vencer el almacenamiento en caché de Django y obtener información actualizada sobre cada iteración.

Recientemente implementé el registro de base de datos y, por lo tanto, tuve que introducir el uso de puntos de guardado en mi función de trabajador. De esta manera, si algo sale mal, puedo retroceder a un buen punto de salvamento, ingresar a la base de datos y continuar hasta que llegue a la transaction.commit() final. transaction.commit()

Ahora, a diferencia de mi servidor de desarrollo, el servidor de producción me da el error:

  DatabaseError: (1305, 'SAVEPOINT s140364713719520_x1 does not exist') 

apuntando a una llamada transaction.savepoint_rollback() en un bloque de except (consulte la fuente a continuación). El servidor dev no tiene tales problemas; y el servidor de producción felizmente produce ID de punto de salvar si escribo transaction.savepoint() en un shell interactivo.

Este es el resumen de mi código , si fuera de alguna ayuda; He tratado de mantenerlo conciso.

Si hay algún gurú de Python benévolo por ahí, por favor, ayúdame. Estoy realmente frustrado por esto, aunque creo que estoy haciendo un buen trabajo manejándolo de una manera tranquila.

Tuve el mismo error desagradable ocasionalmente recurrente:

 OperationalError: (1305, 'SAVEPOINT {{name}} does not exist') 

y Google no lo hizo más claro, excepto que se trata de un problema de concurrencia “normal”. Por lo tanto, no es determinista y es difícil de reproducir en un entorno de desarrollo.

Afortunadamente, pude localizarlo haciendo que el registro de la aplicación de producción fuera lo suficientemente detallado.

Porque

En MySQL hay algunas operaciones que podrían terminar implícitamente una transacción:

  • La statement DDL (por ejemplo, CREATE TABLE , ALTER TABLE , etc.) da como resultado un compromiso implícito. Es bien sabido que los DDL en MySQL no son transaccionales,
  • OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction') y OperationalError: (1205, 'Lock wait timeout exceeded; try restarting transaction') produce una reversión implícita.

Así que el segundo caso resulta de hecho algo “normal”. Podría estar representado por el siguiente código:

 # db is an example database connection object, which # - supports nested (stacked) transactions, # - has autocommit on. db.begin() # START TRANSACTION try: # no-conflict op db.update() db.begin() # SAVEPOINT sp1 try: # conflict op, # eg attempt to change exclusively locked rows by another transaction db.update() db.commit() # RELEASE SAVEPOINT sp1 except: # Everything interesting happens here: # - the change attempt failed with OperationalError: (1213, 'Deadlock...'), # - the transaction is rolled back with all the savepoints, # - next line will attempt to rollback to savepoint which no longer exists, # - so will raise OperationalError: (1305, 'SAVEPOINT sp1 does not exist'), # - which will shadow the original exception. db.rollback() # ROLLBACK TO SAVEPOINT sp1 raise db.commit() # COMMIT except: db.rollback() # ROLLBACK raise 

Actualizar

Tenga en cuenta que Python 2 mencionó lo anterior sobre el sombreado de excepciones. Python 3 implementa el encadenamiento de excepciones y, en caso de un punto muerto, el rastreo tendrá toda la información relevante.

Si revisa los documentos en Django relacionados con los puntos de guardado , se menciona que no todos los motores de almacenamiento MySQL los admiten. Básicamente, MyISAM no maneja las transacciones, por lo que no puede realizar la reversión y InnoDB lo hace. Por lo tanto, me gustaría comprobar que tanto las tablas de desarrollo como las de producción utilizan el mismo tipo de motor de almacenamiento. Puedes comprobarlo ejecutando:

 SHOW CREATE TABLE mytable