2009-06-03 7 views
10

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

+2

+1 para su buena descripción del informe de error –

+8

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

+3

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

Respuesta

7

¿Por qué no les importa a nadie sobre esto fallo MySQLdb?

errores pueden tomar un tiempo para priorizar, investigar, verificar el problema, encontrar una solución, probar la solución, asegúrese de que la solución no interrumpa nada más. Le sugiero que implemente una solución alternativa, ya que podría tomar algo de tiempo para que esta solución llegue por usted.

+3

Todavía no entiendo la falta de comentarios (0, esto es en hecho la primera respuesta que tengo sobre este tema). Realmente parece que nadie lo ha leído. Actualmente estoy usando el parche que cargué en el boleto, que es "worksforme". – Koba

+3

Curiosamente, aunque su error se informó desde 2009-04-20. ¡Alguien debería responder al menos! –

Cuestiones relacionadas