Non ci sono bug e non credo che tu stia fraintendendo nulla; ti mancano solo un paio di pezzi del puzzle.
Le chiavi esterne vengono implementate internamente utilizzando il blocco a livello di riga; a partire da Postgres 8.1 e fino a 9.2, ogni volta che aggiorni la tabella di riferimento (apples
in questo caso), viene generata una query che esegue SELECT FOR SHARE
nella tabella di riferimento (trees
). In modo che SELECT FOR UPDATE
nella prima transazione blocca il SELECT FOR SHARE
dell'integrità referenziale per la seconda transazione. Questo è ciò che causa il blocco nel secondo comando.
Ora ti sento urlare:"Aspetta! Come mai si blocca sul secondo comando e non sul primo? La spiegazione è semplice, davvero -- solo perché c'è una semplice ottimizzazione che salta il SELECT FOR SHARE
interno quando la chiave non viene modificata. Tuttavia, questo è semplicistico in quanto se aggiorni una tupla una seconda volta, questa ottimizzazione non si attiverà perché è più difficile rintracciare i valori originali. Da qui il blocco.
Potresti anche chiederti perché ho detto che questo è fino a 9.2 --- che cosa è con 9.3? La differenza principale è che in 9.3 utilizza SELECT FOR KEY SHARE
, che è un nuovo livello di blocco più leggero; consente una migliore concorrenza. Se provi il tuo esempio in 9.3 e cambia anche il SELECT FOR UPDATE
a SELECT FOR NO KEY UPDATE
(che è una modalità più leggera di SELECT FOR UPDATE
che dice che forse aggiornerai la tupla, ma prometti di non modificare la chiave primaria e prometti di non eliminarla), dovresti vedere che non si blocca. (Inoltre, puoi provare un UPDATE sulla riga di riferimento e se non modifichi la chiave primaria, anche questa non si bloccherà.)
Questa roba 9.3 è stata introdotta da una patch da parte tua veramente come http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=0ac5ad5134f2769ccbaefec73844f8504c4d6182 e penso che sia stato un trucco piuttosto interessante (il messaggio di commit ha alcuni dettagli in più, se ti interessa questo genere di cose). Ma attenzione, non utilizzare versioni precedenti alla 9.3.4 perché quella patch era così estremamente complessa che alcuni bug seri sono passati inosservati e sono stati corretti solo di recente.