Il n'y a pas de bogue, et je ne pense pas que vous compreniez mal quoi que ce soit; il vous manque juste quelques pièces du puzzle.
Les clés étrangères sont implémentées en interne à l'aide du verrouillage au niveau de la ligne ; à partir de Postgres 8.1 et jusqu'à 9.2, chaque fois que vous mettez à jour la table de référencement (apples
dans ce cas), une requête est lancée qui fait SELECT FOR SHARE
sur la table référencée (trees
). Alors que SELECT FOR UPDATE
dans la première transaction bloque le SELECT FOR SHARE
de l'intégrité référentielle pour la seconde transaction. C'est ce qui provoque le blocage dans la deuxième commande.
Maintenant, je vous entends crier :« Attendez ! Comment se fait-il qu'il bloque sur la deuxième commande et pas sur la première ? L'explication est simple, vraiment - c'est juste parce qu'il y a une optimisation simple qui ignore le SELECT FOR SHARE
interne lorsque la clé n'est pas modifiée. Cependant, c'est simpliste dans la mesure où si vous mettez à jour un tuple une deuxième fois, cette optimisation ne se déclenchera pas car il est plus difficile de retrouver les valeurs d'origine. D'où le blocage.
Vous vous demandez peut-être aussi pourquoi j'ai dit que c'était jusqu'à 9.2 --- qu'est-ce qu'il y a avec 9.3 ? La principale différence est qu'en 9.3, il utilise SELECT FOR KEY SHARE
, qui est un nouveau niveau de verrouillage plus léger ; il permet une meilleure concurrence. Si vous essayez votre exemple dans 9.3 et modifiez également le SELECT FOR UPDATE
à SELECT FOR NO KEY UPDATE
(qui est un mode plus léger que SELECT FOR UPDATE
qui dit que vous allez peut-être mettre à jour le tuple, mais vous promettez de ne pas modifier la clé primaire et de ne pas la supprimer), vous devriez voir qu'il ne bloque pas. (Vous pouvez également essayer une mise à jour sur la ligne référencée et si vous ne modifiez pas la clé primaire, elle ne bloquera pas non plus.)
Ce truc 9.3 a été introduit par un patch par votre serviteur en tant que http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 et je pense que c'était un hack plutôt cool (le message de validation contient plus de détails, si vous vous souciez de ce genre de choses). Mais attention, n'utilisez pas de versions antérieures à la 9.3.4 car ce patch était tellement complexe que quelques bugs sérieux sont passés inaperçus et que nous n'avons corrigés que récemment.