[Python] psycopg, cursori e connessioni: tenere occupato un id

Daniele Varrazzo piro a develer.com
Lun 16 Maggio 2011 21:52:59 CEST


On Mon, 16 May 2011 19:37:21 +0200, Marco Fochesato <marcofoc a libero.it>
wrote:

> No, niente buchi.
> Allora, spieghiamoci così:
> siamo di fronte ad un gestionale.

come sempre :)

> L'utente x parte per compilare un DDT.

Problemi con le zanzare?

> L'applicazione "prenota" un id (chiave primaria) per il documento.
> L'utente y parte anche lui per compilare un DDT.
> L'applicazione "prenota" anche per lui un id, lo stesso di quello sopra
> (visto che non è ancora stato chiuso, e potrebbe anche darsi che x
> decida di non chiuderlo e annullare tutto.. (qui è il nocciolo)).
> Il primo che finisce e chiude il ddt, si prende il numero (commit).
> Per il secondo, verrà sollevata una eccezzione e verrà concesso un altro
> id (non mi interessa qui stabilire come).

Non mi sembra il migliore dei comportamenti: perché far fallire la seconda
transazione quando ci sono almeno diversi modi di farla riuscire?

E se il secondo finisce prima del primo? Dovrebbe fallire il primo?

> Quello che mi interessa è la "prenotazione".

Se vuoi davvero prenotare un record, puoi inserire un record incompleto
oppure tenere una tabella separata delle prenotazioni. Forse anche gli
advisory lock possono essere utili.

> Io ho bisogno, durante la compilazione del documento, di testare degli
> inserimenti, e vorrei che l'applicazione ragionasse come se l'id fosse
> inserito. Ma se arriva y, che l'id fosse disponibile anche per lui.

Per fare quello che vuoi tu ha ragione Manlio: usare qualcosa tipo max(id)
+ 1 è la soluzione migliore. Manlio però si è dimenticato di dire di
prendere un lock sulla tabella (in pratica gli inserimenti devono essere
serializzati):

1) quando il documento viene prenotato:

   a) prendi un lock sulla tabella che conflitti con se stesso, per
esempio "LOCK TABLE blah IN EXCLUSIVE MODE".
   b) usa come prossimo id "SELECT max(id) FROM blah"
   c) commit sulla connessione, in modo da liberare il lock.

2) quando il documento viene salvato:

   a) prendi lo stesso lock sulla stessa tabella
   b) prendi lo stesso max
   c) se il max è diverso da quanto registrato, tira un eccezione.

O alternativamente scrivi direttamente il max nel database e aspettati
un'eccezione se c'è una constraint UNIQUE su quel campo.

Questa è un'implementazione pessima di un lock pessimistico: se uno
comincia un'operazione, qualunque operazione concorrente è destinata a
fallire: perché tu voglia fare questo onestamente mi sfugge.

La documentazione sui lock
(http://www.postgresql.org/docs/9.1/static/explicit-locking.html) e sul
comando LOCK (http://www.postgresql.org/docs/9.1/static/sql-lock.html) può
esserti utile.

Lasciare aperta la transazione per tutta la durata dell'edit è qualcosa da
NON fare.


-- 
Daniele Varrazzo - Develer S.r.l. 
http://www.develer.com


Maggiori informazioni sulla lista Python