Studio Basso Amministrazione condomini - Sviluppo software  

Concorrenza ottimistica e DataSet

Molte applicazioni gestiscono ed elaborano dati che hanno bisogno di essere conservati al di là del tempo di esecuzione del programma su un computer, e normalmente si basano su database relazionali per conservare i dati con cui lavorano, per recuperarli in tempi adeguatamente rapidi, per garantire l'integrità dei dati e per sfruttare servizi di base dei server di database (backup, ridondanza dei server nei cluster, repliche distribuite in siti geograficamente distanti).

L'accesso contemporaneo ai dati da parte di più utenti è fonte di rischi che vanno affrontati con un approccio accorto da parte di chi disegna le applicazioni. Un esempio di questi rischi è il caso in cui due utenti, chiamiamoli Anna e Bruno, leggano entrambi lo stesso record per mezzo dell'applicazione. Entrambi gli utenti vedono la stessa versione originale dei dati, per esempio un campo di testo che descrive le caratteristiche di un prodotto offerto dall'azienda in cui lavorano. Anna intende modificare la parte del testo in cui si parla delle caratteristiche estetiche del prodotto. Bruno intende modificare la parte del testo in cui vengono descritte alcune caratteristiche tecniche. Gli scenari possibili sono i seguenti:

Concorrenza pessimistica

Anna comincia a modificare i propri dati, e l'applicazione crea un lock sul record; se Bruno tenta di cominciare anch'egli le modifiche allo stesso record, verrà bloccato dall'applicazione che non riuscirà ad ottenere il lock sullo stesso record.

Una volta che Anna avrà sbloccato il record, Bruno potrà ottenere il lock sul record, leggere la versione del record con le modifiche apportate da Anna, aggiungere le proprie modifiche e aggiornare il database. Al termine di questa operazione, il record conterrà sia le modifiche di Anna che quelle di Bruno.

Concorrenza ottimistica

Anna comincia a modificare i propri dati, senza che l'applicazione crei lock sul record. Anche Bruno comincia a modificare gli stessi dati. Anna andrà ad aggiornare il record con le proprie modifiche. Bruno tenterà di aggiornare lo stesso record; l'applicazione che esegue l'aggiornamento avrà cura di verificare che non ci sono state modifiche tra il momento in cui Bruno ha letto i dati e il momento in cui li ha aggiornati con le proprie modifiche. Siccome nel nostro caso Anna ha modificato il record letto da Bruno, l'applicazione comunicherà a Bruno che l'aggiornamento del record non è andato a buon fine, a causa della modifica dello stesso record apportata da un altro utente (Anna). A questo punto Bruno potrebbe rileggere il record, apportare di nuovo le stesse modifiche, e se nessun altro apporta altre modifiche allo stesso record, Bruno riuscirà a salvare il record che conterrà le modifiche di entrambi gli utenti. L'applicazione avrebbe anche potuto proporre a Bruno di sovrascrivere le modifiche apportate da Anna (probabilmente mostrandogliele), oppure avrebbe potuto tentare di integrare le modifiche di entrambi gli utenti. In ogni caso, di fronte al conflitto di versioni, l'applicazione si dovrebbe rivolgere a Bruno per sapere come comportarsi.

L'ultimo vince

Anna e Bruno leggono la stessa versione del record, entrambi modificano la versione originale del record. Anna salva le proprie modifiche per prima, Bruno le salva per secondo, e l'applicazione sovrascrive le modifiche apportate da Anna, senza ulteriori notifiche, né ad Anna, né a Bruno.

Concorrenza e dati salvati su file system

L'esempio di cui abbiamo parlato è relativo a dati che risiedono in un database, ma gli stessi scenari di aggiornamento dei dati sono in teoria possibili anche quando i dati dell'applicazione sono salvati sul file system, ad esempio in una cartella condivisa in rete, e più utenti tentano di modificare contemporaneamente lo stesso file. Anche in questo caso l'applicazione può decidere quale degli approcci adottare. Word ed Excel optano per il lock di tipo pessimistico: il secondo utente che apre il file lo trova lockato, e può decidere di aprire il file in sola lettura. La maggior parte delle altre applicazioni che salvano i dati su file system usa l'approccio "l'ultimo vince" (a partire dal Blocco note di Windows...). Questo approccio può essere una scelta consapevole da parte dello sviluppatore, oppure più comunemente non si pensa a gestire l'accesso concorrente allo stesso file perché si reputa il problema poco importante per l'applicazione che si sta sviluppando.

Pregi e difetti dei tre approcci

Il problema dell'accesso concorrente ai dati è avvertito soprattutto quando ci sono molti utenti che accedono contemporaneamente agli stessi dati, e quindi quando questi risiedono su database, perciò limiteremo l'analisi dei pro e dei contro dei tre approcci a questo caso.

L'approccio della concorrenza pessimistica è quello che risparmia all'utente degli input che potrebbero essere inutili in caso di conflitti: mentre Anna modifica la propria copia dei dati, a Bruno è impedito di modificare lo stesso record, dato che Anna potrebbe decidere di salvare le proprie modifiche e costringere Bruno a ricominciare dalla nuova versione che comprende le modifiche di Anna. D'altro canto, questo è anche l'approccio più dispendioso in termini di risorse lato server, se gli utenti che eseguono operazioni contemporanee comincia ad essere significativo. E' applicabile solo se il database server supporta i lock (tutti i database server che si rispettino li supportano). Questo è un approccio poco naturale per lo sviluppo di applicazioni web, mentre può essere più facile da implementare in applicazioni desktop.

La strategia della concorrenza ottimistica è quella più naturale per le applicazioni web, in cui non è necessario (anzi, è inopportuno) mantenere aperta una connessione al database per tempi che vanno oltre il tempo impiegato per elaborare la singola richiesta. Può essere utile anche nelle versioni desktop di una applicazione con due versioni di interfaccia utente (applicazione web e desktop) se si decide che è meglio unificare gli approcci nei due casi, o se si decide che è meglio evitare di mantenere aperte le connessioni al database più dello stretto necessario. Le query di aggiornamento si complicano perché devono supportare le verifiche che servono a rifiutare aggiornamenti concorrenti.

La strategia dell'"ultimo vince" è indicata solo per casi particolari, in cui non è importante difendersi da modifiche concorrenti. Ha il vantaggio di avere procedure di aggiornamento molto più semplici (la clausola where delle query di aggiornamento contiene solo il valore della chiave primaria).

In sostanza, l'approccio della concorrenza ottimistica è quello che più si adatta a una vasta schiera di situazioni, e quindi è bagaglio fondamentale di uno sviluppatore che si occupa di accesso ai dati.

Accorgersi della modifica concorrente

Abbiamo visto sopra come la modifica di Anna, con l'approccio della concorrenza ottimistica, vada a buon fine, mentre la modifica di Bruno non debba andare a buon fine. Questo può essere ottenuto in modi differenti a seconda anche del database server di cui si dispone. L'approccio magari più complicato, ma implementabile in tutti i casi è quello di tenere contemporaneamente traccia, per i record modificati da Bruno, sia del valore dei campi letto dal database e presentato a Bruno (valore originale), sia del valore dei campi modificati da Bruno (valore corrente). Le query di aggiornamento impostano al valore corrente tutti i campi, limitando il campo d'azione a un solo record, individuato includendo nella clausola where una condizione sui campi che compongono la chiave primaria. Con l'approccio della concorrenza ottimistica si vuole contemporaneamente verificare che nessun altro abbia fatto modifiche allo stesso record, e quindi si include nella clausola where la condizione che verifica che il valore del campo modificato contenga ancora il valore originale, letto in precedenza e mostrato a Bruno per la modifica. Se il valore è ancora lo stesso, la modifica di Bruno andrà a buon fine e la query aggiornerà un record, se invece è stato modificato, la query non aggiornerà alcun record; il numero di record aggiornati è restituito come risultato del comando al database, quindi l'applicazione può comportarsi di conseguenza, segnalando a Bruno, in caso di modifica concorrente, che qualcun altro ha già modificato il record che stava aggiornando lui.

La query eseguita al momento del salvataggio delle modifiche di Bruno, potrebbe essere simile a questa:

			
UPDATE Prodotti SET (Descrizione = "Lettore CD-ROM 52x. Interfaccia IDE.") 
WHERE IdProdotto = 25 AND Descrizione = "Lettore CD-ROM 52x."

Come si può notare, la query contiene sia il valore inserito da Bruno, sia il valore originale letto in precedenza. Ovviamente, in luogo di una query di questo genere, si poteva usare una query parametrica o una stored procedure per ottenere prestazioni migliori.

Quando la tabella contiene più campi, ciascuno di questi compare due volte, una volta dopo la parola chiave SET nella lista dei nuovi valori, e una volta nella clausola WHERE. Una variazione di questa tecnica prevede di verificare che non siano cambiati solamente i campi che sono stati modificati dall'utente. Il vantaggio è che permette a due utenti la modifica contemporanea dello stesso record purché le modifiche avvengano su campi differenti. Lo strato di accesso ai dati dell'applicazione invece può complicarsi un po' se si vuole adattare la lista di parametri passati ai campi modificati dall'utente.

La tecnica di conservare e passare alla fonte dati i valori originali può essere poco efficiente quando la tabella da aggiornare ha parecchi campi: il numero di parametri per le query di aggiornamento può essere da doppio a triplo rispetto alle query dell'approccio "l'ultimo vince". Una soluzione differente prevede che sulla tabella esista un campo aggiuntivo che contiene un valore modificato automaticamente dal database server ad ogni modifica di un record. L'applicazione anziché doversi conservare sia i valori originali che i valori correnti dei campi, si può conservare solamente i valori correnti: per controllare che nessuno abbia modificato uno qualsiasi dei campi del record è sufficiente includere nella clausola WHERE solamente la condizione che il campo TimeStamp contenga ancora il valore letto in precedenza. La query si semplifica si riduce anche lo scambio dei dati tra client e server.

Il DataSet per la cache di dati locale

In ciascuno dei casi, può essere utile ricorrere al dataset come cache di dati locale: i vantaggi sono la somiglianza di questa struttura dati al database, la presenza di strumenti di mapping automatici tra i dati nel database e quelli negli oggetti DataRow, la possibilità di usare facilmente questo oggetto insieme a controlli windows forms o web forms per presentare i dati all'utente. L'alternativa è scriversi da zero i propri business objects, cosa che può indubbiamente avere dei vantaggi, come quello di evitare l'uso di oggetti che possono essere piuttosto pesanti quali il dataset tipizzato, o la costrizione dei propri oggetti in tipi di dato "da tabella". Non voglio dilungarmi su questo punto, ma se la vostra scelta è quella di utilizzare il dataset per rappresentare i vostri dati in memoria, vi troverete in regalo un aiuto al supporto per la concorrenza ottimistica: l'oggetto DataRow presenta agevolmente la versione corrente del dato, sia in lettura che in modifica, e supporta la conservazione e la lettura del valore originale del dato, utilizzabile al momento in cui si va ad aggiornare la fonte dati con una query che includa i controlli dell'approccio concorrenza ottimistica.

 

 

  Mappa del sito - Note legali