Não há bug, e não acho que você esteja entendendo mal alguma coisa; você está apenas perdendo algumas peças do quebra-cabeça.
As chaves estrangeiras são implementadas internamente usando o bloqueio em nível de linha; a partir do Postgres 8.1 e até 9.2, sempre que você atualizar a tabela de referência (
apples
neste caso), é acionada uma consulta que faz SELECT FOR SHARE
na tabela referenciada (trees
). Para que SELECT FOR UPDATE
na primeira transação bloqueia o SELECT FOR SHARE
da integridade referencial para a segunda transação. Isso é o que causa o bloqueio no segundo comando. Agora eu ouço você gritar:“Espere! Como é que ele bloqueia no segundo comando e não no primeiro? A explicação é simples, na verdade -- isso é só porque há uma otimização simples que pula o
SELECT FOR SHARE
interno quando a chave não está sendo modificada. No entanto, isso é simplista, pois se você atualizar uma tupla uma segunda vez, essa otimização não será acionada porque é mais difícil rastrear os valores originais. Daí o bloqueio. Você também pode estar se perguntando por que eu disse que isso é até 9,2 --- o que há com 9,3? A principal diferença é que no 9.3 ele usa
SELECT FOR KEY SHARE
, que é um novo nível de bloqueio mais leve; permite uma melhor concorrência. Se você tentar seu exemplo em 9.3 e também alterar o SELECT FOR UPDATE
para SELECT FOR NO KEY UPDATE
(que é um modo mais leve que SELECT FOR UPDATE
que diz que talvez você vá atualizar a tupla, mas promete não modificar a chave primária e promete não excluí-la), você deve ver que ela não bloqueia. (Além disso, você pode tentar um UPDATE na linha referenciada e, se não modificar a chave primária, ela também não será bloqueada.) Este material 9.3 foi introduzido por um patch seu verdadeiramente como http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 e eu acho que foi um hack bem legal (A mensagem do commit tem mais alguns detalhes, se você se importa com esse tipo de coisa). Mas cuidado, não use versões anteriores a 9.3.4 porque esse patch era tão complexo que alguns bugs sérios passaram despercebidos e só corrigimos recentemente.