2012-06-29 30 views
6

Necesito bloquear una tabla completa (no una sola fila) con doctrina, me gustaría hacer esto sin consultas nativas si es posible.Cómo bloquear una tabla completa en symfony2 con doctrine2?

La documentación para pessimistic locking sólo describe cómo bloquear entidades específicas a través de estos métodos:

  • EntityManager # find
  • EntityManager # bloquear
  • consulta # setLockMode

tengo una transacción que necesita insertar una fila cuyos valores dependen de los valores del resto de las filas en la tabla, así que debo evitar que dos transacciones realicen una al mismo tiempo en esa mesa.

Estoy usando una demarcación de transacción explícita, que se supone que funciona bien con el bloqueo (de acuerdo con la documentación anterior).

NOTA: El bloqueo optimista no es lo suficientemente bueno en este caso, no puedo permitirme volver a intentar la transacción. Además, la consulta no debe ser lenta, por lo que el rendimiento no es un problema.

EDIT: Voy a dar un ejemplo. Imagine que desea construir un autoincremento a mano, y debe seleccionar max() de la tabla para obtener el resultado anterior para insertar el siguiente. Debe asegurarse de que no haya dos transacciones que intenten insertar el mismo valor en caso de que seleccionen max() al mismo tiempo.

Estoy buscando una solución general a este problema cuando auto_increment no es bueno, por ejemplo con cadenas, o columnas múltiples, hash o cualquier cálculo que tenga que hacer en la fila anterior.

El bloqueo es una solución sólida y, a diferencia del bloqueo optimista, no tiene que volver a intentar errores.

Entonces, ¿hay alguna manera de utilizar el bloqueo de tabla en doctrina?

+0

Imo, una mejor opción sería la de abrir una transacción, busque los datos de la tabla que necesita a través de una consulta de selección y luego realice la actualización. – JamesHalsall

+0

El problema es que si una segunda transacción se produce antes de que la primera se comprometa, la segunda transacción insertará un valor no válido. El resultado de la segunda transacción también debe depender de la fila insertada por la primera. Es por eso que necesito bloquear, las transacciones no deben superponerse para esa tabla. – Jens

Respuesta

1

Al mirar la documentación en Doctrine 2.x, no creo que haya una forma admitida de bloquear toda la tabla. Por supuesto, podría intentar bloquear todos los registros a través de Doctrine individualmente, pero esto sería engorroso y no es realmente una buena idea.

En su lugar, me gustaría utilizar el gestor de entidades Doctrina para ejecutar SQL prima en la base de datos ...

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

y después de que haya terminado con la actualización de su ...

$em->getConnection()->exec('UNLOCK TABLES;');

EDIT:

de MySQL documentation de bloqueos de tabla ...

  • La sesión que mantiene el bloqueo puede leer y escribir la tabla.

  • Solo la sesión que contiene el bloqueo puede acceder a la tabla.Ninguna otra sesión puede acceder a ella hasta que se libere el bloqueo.

  • Bloquea las solicitudes de la tabla por el bloqueo de otras sesiones mientras se mantiene el bloqueo de ESCRITURA.

creo que el segundo punto aquí es la clave, sólo su sesión puede leer/escribir en esa tabla.

+0

Además, bloquear los registros individualmente no evitará que se inserten nuevos registros. OTOH, la instrucción LOCK no es transaccional de acuerdo con los documentos, y no estoy seguro de cómo la doctrina implementa las transacciones, pero si usa START TRANSACTION, no va a funcionar. – Jens

+0

'LOCK TABLES [tbl_name]' bloqueará toda la tabla para escritura, vea mi edición – JamesHalsall

+0

Sí, lo sé, el único problema que tengo con LOCK es que solo funciona bien cuando hace transacciones usando 'autocommit = 0', y yo No estoy seguro de cómo la doctrina maneja las transacciones. Todavía tengo que probarlo. Consulte este http://dev.mysql.com/doc/refman/5.0/es/lock-tables-and-transactions.html – Jens

1

Posiblemente duplicado por Doctrine2 ORM select for update

Aquí hay un código relacionado:

try { 
    $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); 

    // do the work 

    $em->flush(); 
} catch(OptimisticLockException $e) { 
    echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; 
} 

El LockMode :: parámetro OPTIMISTA podría proporcionar lo que necesita.

+0

En general, la selección de fila para la actualización no impide las inserciones simultáneas. –

2

Siguiendo el consejo hasta el momento, yo probamos este:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access 

// calculate $new_number... 

// persist $new_number on table_name... 
$table_name->setCalculatedNumber($new_number); 
$em->persist($table_name); 
$em->flush(); 

$em->getConnection()->exec('UNLOCK TABLES;'); 

he comprobado con JMeter, y el bloqueo no estaba funcionando con una carga pesada (16 solicitudes/seg). El seguimiento mostró que otras instancias obtuvieron el bloqueo antes de que se hubiera abandonado explícitamente. El problema (como lo sugirió Jens) fue que flush() comienza implícitamente con START TRANSACTION, lo que desactiva el bloqueo de la tabla. Usando una actualización nativa solucionado el problema para mí:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access 

// calculate $new_number... 

// persist $new_number on table_name... 
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");  

$em->getConnection()->exec('UNLOCK TABLES;'); 
$em->refresh($table_name); 

fue necesario realizar la actualización de arrastre() para hacer que el número calculado disponible en consulta posterior resulta

Cuestiones relacionadas