TL; DR: He proporcionado un parche para un error que encontré y tengo 0 comentarios sobre él. Me pregunto si es un error en absoluto. Esto no es una diatriba Lea esto y, si se ve afectado, verifique la solución.¿Por qué a nadie le importa este error de MySQLdb? ¿Es un error?
he encontrado y reportado este error MySQLdb Hace algunas semanas (edit: Hace 6 semanas), envió un parche, publicado en un par de foros de ORM, enviado por correo el autor MySQLdb, enviado por correo a algunas personas hablar de los puntos muertos de manipulación, enviado por correo Los autores de ORM y todavía estoy esperando cualquier tipo de comentario.
Este error me causó mucha pena y las únicas explicaciones que puedo encontrar en los comentarios son que o bien nadie usa "SELECCIONAR ... PARA ACTUALIZAR" en python con mysql o que esto no es un error.
Básicamente, el problema es que los bloqueos y las excepciones "espera de tiempo de espera de bloqueo" NO se plantean al emitir un "SELECCIONAR ... PARA ACTUALIZAR" utilizando un cursor MySQLdb. En cambio, la instrucción falla silenciosamente y devuelve un conjunto de resultados vacío, que cualquier aplicación interpretará como si no hubiera filas coincidentes.
He probado la versión de SVN y todavía está afectada. Probado en las instalaciones predeterminadas de Ubuntu Intrepid, Jaunty y Debian Lenny y los afectados también. La versión actual instalada por easy_install (1.2.3c1) se ve afectada.
Esto afecta a SQLAlchemy y SQLObject también y probablemente cualquier ORM que usó cursores MySQLdb se ve afectado también.
Este script puede reproducir un punto muerto que activará el error (sólo cambia el usuario/contraseña en get_conn, creará las tablas necesarias):
import time
import threading
import traceback
import logging
import MySQLdb
def get_conn():
return MySQLdb.connect(host='localhost', db='TESTS',
user='tito', passwd='testing123')
class DeadlockTestThread(threading.Thread):
def __init__(self, order):
super(DeadlockTestThread, self).__init__()
self.first_select_done = threading.Event()
self.do_the_second_one = threading.Event()
self.order = order
def log(self, msg):
logging.info('%s: %s' % (self.getName(), msg))
def run(self):
db = get_conn()
c = db.cursor()
c.execute('BEGIN;')
query = 'SELECT * FROM locktest%i FOR UPDATE;'
try:
try:
c.execute(query % self.order[0])
self.first_select_done.set()
self.do_the_second_one.wait()
c.execute(query % self.order[1])
self.log('2nd SELECT OK, we got %i rows' % len(c.fetchall()))
c.execute('SHOW WARNINGS;')
self.log('SHOW WARNINGS: %s' % str(c.fetchall()))
except:
self.log('Failed! Rolling back')
c.execute('ROLLBACK;')
raise
else:
c.execute('COMMIT;')
finally:
c.close()
db.close()
def init():
db = get_conn()
# Create the tables.
c = db.cursor()
c.execute('DROP TABLE IF EXISTS locktest1;')
c.execute('DROP TABLE IF EXISTS locktest2;')
c.execute('''CREATE TABLE locktest1 (
a int(11), PRIMARY KEY(a)
) ENGINE=innodb;''')
c.execute('''CREATE TABLE locktest2 (
a int(11), PRIMARY KEY(a)
) ENGINE=innodb;''')
c.close()
# Insert some data.
c = db.cursor()
c.execute('BEGIN;')
c.execute('INSERT INTO locktest1 VALUES (123456);')
c.execute('INSERT INTO locktest2 VALUES (123456);')
c.execute('COMMIT;')
c.close()
db.close()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
init()
t1 = DeadlockTestThread(order=[1, 2])
t2 = DeadlockTestThread(order=[2, 1])
t1.start()
t2.start()
# Wait till both threads did the 1st select.
t1.first_select_done.wait()
t2.first_select_done.wait()
# Let thread 1 continue, it will get wait for the lock
# at this point.
t1.do_the_second_one.set()
# Just make sure thread 1 is waiting for the lock.
time.sleep(0.1)
# This will trigger the deadlock and thread-2 will
# fail silently, getting 0 rows.
t2.do_the_second_one.set()
t1.join()
t2.join()
La salida de la ejecución de este en una MySQLdb sin parchear es esto:
$ python bug_mysqldb_deadlock.py
INFO:root:Thread-2: 2nd SELECT OK, we got 0 rows
INFO:root:Thread-2: SHOW WARNINGS: (('Error', 1213L, 'Deadlock found when trying to get lock; try restarting transaction'),)
INFO:root:Thread-1: 2nd SELECT OK, we got 1 rows
INFO:root:Thread-1: SHOW WARNINGS:()
se puede ver que Rosca-2 tiene 0 filas de una tabla sabemos que tiene 1 y sólo la emisión de una declaración "vER ADVERTENCIAS" se puede ver lo que pasó. Si marca "SHOW ENGINE INNODB STATUS", verá esta línea en el registro "*** WE ROLL BACK TRANSACTION (2)", todo lo que sucede después de la selección fallida en Thread-2 se realiza en una transacción medio revertida.
Después de aplicar el parche (comprobar el billete para ella, URL a continuación), esta es la salida de la ejecución del script:
$ python bug_mysqldb_deadlock.py
INFO:root:Thread-2: Failed! Rolling back
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "bug_mysqldb_deadlock.py", line 33, in run
c.execute(query % self.order[1])
File "/home/koba/Desarollo/InetPub/IBSRL/VirtualEnv-1.0-p2.4/lib/python2.4/site-packages/MySQL_python-1.2.2-py2.4-linux-x86_64.egg/MySQLdb/cursors.py", line 178, in execute
self.errorhandler(self, exc, value)
File "/home/koba/Desarollo/InetPub/IBSRL/VirtualEnv-1.0-p2.4/lib/python2.4/site-packages/MySQL_python-1.2.2-py2.4-linux-x86_64.egg/MySQLdb/connections.py", line 35, in defaulterrorhandler
raise errorclass, errorvalue
OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')
INFO:root:Thread-1: 2nd SELECT OK, we got 1 rows
INFO:root:Thread-1: SHOW WARNINGS:()
En este caso se produce una excepción en Hilo-2 y se deshace correctamente.
Entonces, ¿cuál es su opinión ?, ¿esto es un error? a nadie le importa o solo estoy loco?
Este es el billete que abrí en SF: http://sourceforge.net/tracker/index.php?func=detail&aid=2776267&group_id=22307&atid=374932
+1 para su buena descripción del informe de error –
Apenas veo cómo una pregunta de SO puede ayudar aquí. Si tienes un parche que funcione, bien para ti, úsalo. Pero no olvide que los desarrolladores de OSS tienen un trabajo real, una vida privada y, a veces, simplemente lleva tiempo tratar los atascos de errores. El propietario recibió una notificación cuando envió un informe de error, vio la notificación. Aunque cuanto más te quejas, menos le parecerá atractiva la perspectiva de arreglar el error mencionado. Sea paciente. E incluso si soluciona esto en un mes, di gracias. – NicDumZ
No envié a la misma persona más de 1 mensaje sobre este tema. En lugar de hacerlo, pregunté a diferentes personas y la razón por la que estoy preguntando aquí es para evitar preguntarle a las mismas personas que ya hice. Proporcioné una solución y me gustaría recibir algún comentario de alguien con autoridad sobre este tema para saber si está bien o no. – Koba