2008-11-06 20 views
8

Quiero hacer que mi biblioteca de Python trabaje con MySQLdb para poder detectar interbloqueos y volver a intentarlo. Creo que he codificado una buena solución y ahora quiero probarla.Cómo puedo causar un punto muerto en MySQL para propósitos de prueba

¿Alguna idea para las consultas más simples que podría ejecutar usando MySQLdb para crear una condición de interbloqueo sería?

Información instalaciones:

  • MySQL 5.0.19
  • cliente 5.1.11
  • Windows XP
  • Python 2.4/1.2.1 MySQLdb p2

Respuesta

1

siempre se puede ejecutar LOCK TABLE tablename de otra sesión (mysql CLI por ejemplo). Eso podría hacer el truco.

Permanecerá bloqueado hasta que lo libere o desconecte la sesión.

+0

Eso solo causaría un error operacional: (1205, 'Se agotó el tiempo de espera de espera de bloqueo; intente reiniciar la transacción'), ¿no? – Greg

+0

@Greg: Creo que están hablando de una sesión haciendo LOCK TABLE A, y otra sesión haciendo LOCK TABLE B. Deben sincronizarse de alguna manera en este momento. Luego, la sesión uno intenta una TABLA DE BLOQUEO B. Cuando la sesión dos intenta BLOQUEAR LA TABLA A, bloqueará. –

1

No estoy familiarizado con Python, así que disculpe mi lenguaje incorrecto Si digo esto mal ... pero abra dos sesiones (en ventanas separadas, o desde procesos de Python separados, desde cajas separadas funcionaría ...) Entonces ...

. En la sesión A:

Begin Transaction 
     Insert TableA() Values()... 

. Luego En Sesión B:

Begin Transaction 
    Insert TableB() Values()... 
    Insert TableA() Values() ... 

. A continuación, volver a la sesión Un

Insert TableB() Values() ... 

Usted obtendrá un punto muerto ...

+0

¿Cómo me aseguraré de que funcionen exactamente al mismo tiempo? – Greg

+0

Disculpa, quise controlar "manualmente" cuando se ejecutan cada uno de los tres pasos anteriores ... Primero, comienza la transacción e inserta la tabulación a en la sesión A, luego pasa al otro proceso y haz el segundo fragmento, luego pasa al primero proceso y (tal vez tener un botón de usuario para hacer esto) ejecutar el último fragmento –

+0

Sin usar Python o cualquier otro código de cliente, simplemente escribiría las instrucciones Insert en dos ventanas SQL, y pasaría de una a otra ... . Pero desea que su código "atrape" y detecte el punto muerto ... –

1

quieres algo a lo largo de las siguientes líneas.

parent.py

import subprocess 
c1= subprocess.Popen(["python", "child.py", "1"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
c2= subprocess.Popen(["python", "child.py", "2"], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
out1, err1= c1.communicate("to 1: hit it!") 
print " 1:", repr(out1) 
print "*1:", repr(err1) 
out2, err2= c2.communicate("to 2: ready, set, go!") 
print " 2:", repr(out2) 
print "*2:", repr(err2) 
out1, err1= c1.communicate() 
print " 1:", repr(out1) 
print "*1:", repr(err1) 
out2, err2= c2.communicate() 
print " 2:", repr(out2) 
print "*2:", repr(err2) 
c1.wait() 
c2.wait() 

child.py

import yourDBconnection as dbapi2 

def child1(): 
    print "Child 1 start" 
    conn= dbapi2.connect(...) 
    c1= conn.cursor() 
    conn.begin() # turn off autocommit, start a transaction 
    ra= c1.execute("UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'") 
    print ra 
    print "Child1", raw_input() 
    rb= c1.execute("UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'") 
    print rb 
    c1.close() 
    print "Child 1 finished" 

def child2(): 
    print "Child 2 start" 
    conn= dbapi2.connect(...) 
    c1= conn.cursor() 
    conn.begin() # turn off autocommit, start a transaction 
    rb= c1.execute("UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'") 
    print rb 
    print "Child2", raw_input() 
    ra= c1.execute("UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'") 
    print ta 
    c1.close() 
    print "Child 2 finish" 

try: 
    if sys.argv[1] == "1": 
     child1() 
    else: 
     child2() 
except Exception, e: 
    print repr(e) 

Nota la simetría. Cada niño comienza teniendo un recurso. Luego intentan obtener el recurso retenido de otra persona. Puede, por diversión, tener 3 hijos y 3 recursos para un círculo realmente vicioso.

Tenga en cuenta que es difícil solucionar una situación en la que se produce un interbloqueo. Si sus transacciones son cortas y constantes, es muy difícil lograr un punto muerto. El punto muerto requiere (a) una transacción que mantenga bloqueos durante un tiempo prolongado Y (b) transacciones que adquieren bloqueos en un orden incoherente. He encontrado que es más fácil evitar interbloqueos manteniendo mis transacciones cortas y consistentes.

También tenga en cuenta el no determinismo. No puede predecir qué niño morirá con un punto muerto y qué continuará después de que el otro muera. Solo uno de los dos debe morir para liberar los recursos necesarios para el otro. Algunos RDBMS afirman que hay una regla basada en la cantidad de recursos que posee, bla, bla, bla, pero en general, nunca sabrá cómo se eligió a la víctima.

Debido a que las dos escrituras están en un orden específico, se espera que primero muera el hijo 1. Sin embargo, no puedes garantizar eso. No es un punto muerto hasta que el niño 2 intente obtener los recursos del niño 1: la secuencia de quién adquirió primero no puede determinar quién muere.

También tenga en cuenta que estos son procesos, no hilos. Los subprocesos, debido a Python GIL, podrían sincronizarse inadvertidamente y requerirían muchas llamadas al time.sleep(0.001) para darle al otro subproceso la oportunidad de ponerse al día. Los procesos, por esto, son un poco más simples porque son completamente independientes.

2

aquí hay algo de pseudocódigo para qué lo hago en PHP:

Guión 1:

START TRANSACTION; 
INSERT INTO table <anything you want>; 
SLEEP(5); 
UPDATE table SET field = 'foo'; 
COMMIT; 

Guión 2:

START TRANSACTION; 
UPDATE table SET field = 'foo'; 
SLEEP(5); 
INSERT INTO table <anything you want>; 
COMMIT; 

Ejecutar secuencia de comandos 1 y luego ejecutar inmediatamente la secuencia de comandos 2 en anoth er terminal. Obtendrá un punto muerto si la tabla de la base de datos ya tiene algunos datos (en otras palabras, inicia el bloqueo después de la segunda vez que prueba esto).

Tenga en cuenta que si mysql no respeta el comando SLEEP(), utilice el equivalente de Python en la aplicación.

Cuestiones relacionadas