2009-06-03 2 views
10

TL; DR: Ho fornito una patch per un bug che ho trovato e ho ricevuto 0 feedback su di esso. Mi chiedo se sia un errore. Questo non è uno sfogo. Si prega di leggere questo e se si può essere interessati da controllare la correzione.Perché nessuno si preoccupa di questo bug MySQLdb? E 'un errore?

Ho trovato e segnalato questo bug MySQLdb alcune settimane fa (modifica: 6 settimane fa), inviato una patch, postato su un paio di forum di ORM, inviato l'autore di MySQLdb, inviato alcune persone che parlano di gestione di deadlock, inviato Autori ORM e sto ancora aspettando qualsiasi tipo di feedback.

Questo errore mi ha causato molto dolore e le uniche spiegazioni che posso trovare sul feedback sono che nessuno usa "SELECT ... FOR UPDATE" in python con mysql o che questo non è un bug.

Fondamentalmente il problema è che le deadlock e le eccezioni di "blocco attesa timeout" NON vengono sollevate quando si emette un "SELECT ... FOR UPDATE" utilizzando un cursore MySQLdb. Invece, l'istruzione fallisce silenziosamente e restituisce un set di risultati vuoto, che qualsiasi applicazione interpreterà come se non ci fossero righe corrispondenti.

Ho provato la versione SVN ed è ancora interessata. Testato sulle installazioni predefinite di Ubuntu Intrepid, Jaunty e Debian Lenny e anche quelle sono interessate. La versione corrente installata da easy_install (1.2.3c1) è interessata.

Questo riguarda anche SQLAlchemy e SQLObject e probabilmente anche qualsiasi ORM che abbia utilizzato i cursori MySQLdb.

Questo script in grado di riprodurre una situazione di stallo che attiverà il bug (basta cambiare l'user/pass in get_conn, creerà le tabelle necessarie):

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() 

L'uscita di correre questo su un MySQLdb senza patch è questo:

$ 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:() 

si può vedere che thread-2 ha ricevuto 0 righe da una tabella sappiamo ha 1 e solo l'emissione di un "MOSTRA AVVERTENZE" l'istruzione è possibile vedere cosa è successo. Se si seleziona "SHOW ENGINE INNODB STATUS", questa riga verrà visualizzata nel registro "*** WE ROLL BACK TRANSACTION (2)", tutto ciò che accade dopo la selezione non riuscita su Thread-2 è su una transazione con ritorno a metà.

Dopo aver applicato la patch (controllare il biglietto per esso, l'URL di seguito), questo è l'uscita dell'esecuzione dello 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:() 

In questo caso viene sollevata un'eccezione on Thread-2 e rotola indietro propriamente.

Quindi, qual è la tua opinione ?, questo è un bug? a nessuno importa o sono solo pazzo?

Questo è il biglietto ho aperto su SF: http://sourceforge.net/tracker/index.php?func=detail&aid=2776267&group_id=22307&atid=374932

+2

+1 per la descrizione del bug report valido –

+8

Difficilmente vedo come una domanda SO può aiutare qui. Se hai una patch funzionante, buona per te, usala. Ma non dimenticare che gli sviluppatori OSS hanno un vero lavoro, una vita privata e talvolta ci vuole solo del tempo per trattare i backlog degli errori. Il proprietario è stato avvisato quando hai inviato una segnalazione di errore, ha visto la notifica. Imho più ti lamenti, meno la prospettiva di fissare il suddetto bug sarà attraente per lui. Sii paziente. E anche se aggiusta questo in un mese, di 'grazie. – NicDumZ

+3

Non ho inviato la stessa persona più di 1 messaggio su questo argomento. Invece di farlo, ho chiesto a persone diverse e la ragione per cui sto chiedendo qui è di evitare di chiedere alle stesse persone che ho già fatto. Ho fornito una correzione e mi piacerebbe ricevere un feedback da qualcuno con autorità in materia per sapere se è ok o no. – Koba

risposta

7

Perché non si preoccupano chiunque questo MySQLdb bug?

i bug possono richiedere un po 'di tempo per stabilire le priorità, ricercare, verificare il problema, trovare una soluzione, testare la correzione, assicurarsi che la correzione non renda nient'altro. Ti suggerirei di implementare un lavoro in giro, poiché potrebbe volerci del tempo prima che questa correzione arrivi per te.

+3

Ancora non capisco la mancanza di feedback (0, questo è in è stata la prima risposta che abbia mai avuto su questo argomento). Sembra davvero che nessuno lo abbia letto. Attualmente sto usando la patch che ho caricato sul ticket quale "worksforme". – Koba

+3

È interessante notare che il suo errore è stato segnalato dal 2009-04-20. Qualcuno dovrebbe rispondere almeno! –