Vuelva a intentar el interlocking para MySQL / SQLAlchemy

He buscado durante bastante tiempo y no puedo encontrar una solución a mi problema. Estamos utilizando SQLAlchemy junto con MySQL para nuestro proyecto y encontramos varias veces el error temido:

1213, ‘Se ha encontrado un interlocking al intentar bloquearse; intente reiniciar la transacción ‘.

Nos gustaría intentar reiniciar la transacción como máximo tres veces en este caso.

Empecé a escribir un decorador que hace esto, pero no sé cómo guardar el estado de la sesión antes de que falle y vuelva a intentar la misma transacción. (Como SQLAlchemy requiere una reversión cada vez que se produce una excepción)

Mi trabajo hasta ahora,

def retry_on_deadlock_decorator(func): lock_messages_error = ['Deadlock found', 'Lock wait timeout exceeded'] @wraps(func) def wrapper(*args, **kwargs): attempt_count = 0 while attempt_count < settings.MAXIMUM_RETRY_ON_DEADLOCK: try: return func(*args, **kwargs) except OperationalError as e: if any(msg in e.message for msg in lock_messages_error) \ and attempt_count <= settings.MAXIMUM_RETRY_ON_DEADLOCK: logger.error('Deadlock detected. Trying sql transaction once more. Attempts count: %s' % (attempt_count + 1)) else: raise attempt_count += 1 return wrapper 

Realmente no se puede hacer eso con la Session desde el exterior. Session tendría que apoyar esto internamente. Implicaría ahorrar mucho estado privado, por lo que puede que no valga la pena.

Olvidé por completo la mayoría de las cosas de ORM en favor de la interfaz de nivel inferior SQLAlchemy Core. Usando esa (o incluso cualquier interfaz dbapi) puede usar de manera trivial su decorador retry_on_deadlock_decorator (vea la pregunta anterior) para hacer un contenedor db.execute tenga en cuenta los db.execute .

  @retry_on_deadlock_decorator def deadlock_safe_execute(db, stmt, *args, **kw): return db.execute(stmt, *args, **kw) 

Y en lugar de

  db.execute("UPDATE users SET active=0") 

tú lo haces

  deadlock_safe_execute(db, "UPDATE users SET active=0") 

que volverá a intentar automáticamente si se produce un punto muerto.

¿Usaste código como este?

 try: Perform table transaction break except: rollback delay try again to perform table transaction 

La única manera de realmente manejar los interlockings es escribir su código para esperarlos. Esto generalmente no es muy difícil si el código de su base de datos está bien escrito. A menudo, puede poner un try / catch alrededor de la lógica de ejecución de la consulta y buscar un punto muerto cuando se producen errores. Si encuentra uno, lo normal es intentar ejecutar la consulta fallida nuevamente.

Enlaces útiles:

  • Cómo hacer frente a los puntos muertos
  • Entendiendo Autocommit
  • Propiedad MySQLConnection.autocommit