No hay error, y no creo que estés malinterpretando nada; solo te faltan un par de piezas del rompecabezas.
Las claves foráneas se implementan internamente mediante el bloqueo a nivel de fila; a partir de Postgres 8.1 y hasta 9.2, siempre que actualice la tabla de referencia (apples
en este caso), se dispara una consulta que hace SELECT FOR SHARE
en la tabla referenciada (trees
). Para que SELECT FOR UPDATE
en la primera transacción bloquea el SELECT FOR SHARE
de la integridad referencial para la segunda transacción. Esto es lo que provoca el bloqueo en el segundo comando.
Ahora te escucho gritar:“¡Espera! ¿Cómo es que se bloquea en el segundo comando y no en el primero? La explicación es simple, en realidad, eso es solo porque hay una optimización simple que omite el SELECT FOR SHARE
interno. cuando la clave no está siendo modificada. Sin embargo, esto es simplista en el sentido de que si actualiza una tupla por segunda vez, esta optimización no se activará porque es más difícil rastrear los valores originales. De ahí el bloqueo.
Quizás también se pregunte por qué dije que esto es hasta 9.2 --- ¿qué pasa con 9.3? La principal diferencia es que en 9.3 usa SELECT FOR KEY SHARE
, que es un nuevo nivel de bloqueo más ligero; permite una mejor concurrencia. Si prueba su ejemplo en 9.3 y también cambia el SELECT FOR UPDATE
para SELECT FOR NO KEY UPDATE
(que es un modo más ligero que SELECT FOR UPDATE
que dice que tal vez va a actualizar la tupla, pero promete no modificar la clave principal y promete no eliminarla), debería ver que no se bloquea. (Además, puede probar una ACTUALIZACIÓN en la fila a la que se hace referencia y, si no modifica la clave principal, tampoco se bloqueará).
Este material 9.3 fue introducido por un parche de su servidor como http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 y creo que fue un truco genial (el mensaje de confirmación tiene algunos detalles más, si te importa ese tipo de cosas). Pero tenga cuidado, no use versiones anteriores a la 9.3.4 porque ese parche era tan complejo que algunos errores graves pasaron desapercibidos y solo los solucionamos recientemente.