2012-04-21 21 views
5

De la documentación para find_or_create:Cómo evitar las condiciones de carrera cuando se usa el método find_or_create de DBIx :: Class :: ResultSet?

Nota: Debido a find_or_create() lee de la base de datos y luego posiblemente insertos en base al resultado, este método está sujeto a una condición de carrera . Otro proceso podría crear un registro en la tabla después de el hallazgo se ha completado y antes de que la creación haya comenzado. Para evitar este problema, use find_or_create() dentro de una transacción.

¿Es suficiente simplemente usar find_or_create() dentro de una transacción en PostgreSQL?

Respuesta

6

No, la documentación es incorrecta. El uso de una transacción sola hace que no evite este problema. Solo garantiza que se revierte toda la transacción si se produce una excepción, de modo que no se conservará ningún estado incoherente en la base de datos.

Para evitar este problema debe bloquear la tabla, dentro de una transacción, porque todos los bloqueos se liberan al final de una transacción. Algo como:

BEGIN; 
LOCK TABLE mytbl IN SHARE MODE; 

-- do your find_or_create here 

COMMIT; 

Pero eso no es una cura mágica para todo. Puede convertirse en un problema de rendimiento, y puede haber deadlocks (transacciones simultáneas que intentan bloquear recursos que el otro ya tiene bloqueados). PostgreSQL detectará dicha condición y cancelará todas menos una de las transacciones competidoras. Debe estar preparado para volver a intentar la operación en caso de falla.

The PostgreSQL manual about locks.

Si usted no tiene una gran cantidad de concurrencia es posible que también acaba de ignorar el problema. El intervalo de tiempo es muy pequeño, por lo que muy pocas veces sucede realmente. Si detecta el error de violación de la clave duplicada, que no causará daños, entonces también habrá cubierto esto.

+2

Otras páginas útiles. Documentos: http://www.postgresql.org/docs/current/interactive/mvcc.html Implementación serializable de PostgreSQL versión 9.1 o posterior: http://wiki.postgresql.org/wiki/SSI Otros niveles de aislamiento o versiones de PostgreSQL: http : //www.postgresql.org/files/developer/concurrency.pdf – kgrittn

+0

¿Pero cuál es la forma correcta de detectar "el error de violación de clave duplicada" en DBIC? –

+0

@eugeney: Supongo que abre una nueva pregunta para eso, en lugar de un comentario. Siempre puedes enlazar a este para ahorrarte algo de tipeo. –

0

Esta implementación de find_or_create debe impedir que la condición de carrera, que se describe en el PO:

eval { 
    $row = $self->model->create({ ... }); 
} 
if([email protected] && [email protected] =~ /duplicate/i) { 
    $row = $self->model->find({ ... }); 
} 

También reduce find_or_create() a una sola consulta en el mejor de los casos.

+1

Esto invierte la lógica. Pero ahora tiene un pequeño intervalo de tiempo en el que la entrada podría eliminarse, en cuyo caso la lógica fallará. Intentar escribir primero es más costoso que tratar de leer. Entonces esto es solo una mejora si los duplicados son muy poco comunes. De cualquier forma, los conflictos deberían ser muy raros, porque el intervalo de tiempo es muy pequeño. –

Cuestiones relacionadas