Consigli su gestione concorrenza

sabato 31 maggio 2014 - 15.43

bluland Profilo | Guru

Salve,

DB sql 2008 o sql 2012.

Dopo aver reperito il prossimo ID da una tabella A, devo eseguire con una transazione degli insert in altre tabelle in cui l'ID recuperato da A è uno dei valori di insert.

Domanda1:
Consigli come gestire al meglio una situazione del genere, calcolando il rischio della concorrenza, ma anche di una transazione andata male e tenendo conto che è un applicazione web che girerà su internet.

Domanda2:
Se avendo lo stesso scenario, prima di effettuare la transazione volessi mostrare a video il prossimo ID, come potrei tenerlo bloccato fino al buon fine della transazione, ed allo stesso tempo:
- gestire altre sessioni
- recuperarlo in una sessione successiva in caso di abbandono della sessione dalla parte dell'utente
- recuperarlo in una sessione successiva in caso di fallimento della transazione

saluti
--------------------
Vincenzo PESANTE
Software Engineer

alx_81 Profilo | Guru

>Salve,
ciao

>Domanda1:
>Consigli come gestire al meglio una situazione del genere, calcolando
>il rischio della concorrenza, ma anche di una transazione andata
>male e tenendo conto che è un applicazione web che girerà su internet.
Dipende molto da come hai disegnato anche le chiavi ed i vincoli. Ma in questo caso è importante capire anche come reperisci il "prossimo" id. Se si tratta di uno SCOPE_IDENTITY() è meglio averlo sotto la stessa transazione (la prima insert per l'identity) delle altre insert (successive). In questo modo, l'id che ottieni è intoccabile dagli altri processi ed è lockata anche la tabella su cui stai inserendo fino alla commit definitiva.
Considera che tu stai inserendo, quindi se non vuoi bloccare tutto (con il rischio di deadlock che aumenta e una maggiore latenza di risposta in condizioni di traffico elevato), ti conviene utilizzare il comportamento di default (read committed) sotto transazione nella insert. Se puoi fare l'inserimento set based invece che row by row riesci ad avere un lock più ampio..
Inoltre dovresti capire cosa vuoi che succeda in caso di errore, qual è la quantità di dati che puoi permetterti di "perdere" o che puoi riproporre.. ecc.
La tua domanda è molto generica mentre ogni caso ha la sua proprietaria gestione.
In linea di massima direi che con i corretti vincoli di PK e altri constraint, potrai utilizzare semplicemente i comportamenti di default forniti da SQL Server, sotto transazione.

>Domanda2:
>Se avendo lo stesso scenario, prima di effettuare la transazione
>volessi mostrare a video il prossimo ID, come potrei tenerlo
>bloccato fino al buon fine della transazione, ed allo stesso
>tempo:
>- gestire altre sessioni
>- recuperarlo in una sessione successiva in caso di abbandono
>della sessione dalla parte dell'utente
>- recuperarlo in una sessione successiva in caso di fallimento
>della transazione
Eh in questo caso devi rilasciare il lock, altrimenti l'id non è accessibile. Ma ovviamente qui cadi in "come cavolo gestisco ora la possibilità di eccezione"?
Metti che qualcosa vada storto a metà.. tutte le cose che hai fatto con quell'id che fine fanno?
Molti di questi casi vanno gestiti in base al tipo di intervento che devi fare in caso di errore.
Puoi anche fare tutto senza transazioni, ma devi avere dei processi di retry, o di gestione dell'errore che ti consentano la "pulizia" ed il "rifacimento" di certe istruzioni.
Ma c'è da fare molta attenzione. Rischi di entrare in un macello di logiche malsane
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

bluland Profilo | Guru

Grazie Alex della risposta.

In effetti a questo punto mi accontento solo di creare lo scenario per la domana1.
Il dominio applicativo è quello di un sistema web che genera Fatture ed il mio problema è come generare fatture senza incorrere in una situazione dove 2 utenti si collegano allo stesso tempo rischiando di creare lo stesso numero di fattura?
In piu dopo la creazione del numero devo andare a fare vari insert (master-details) dove mi porto dietro appunto tale numero come riferimento e se qualcosa andasse male io quel numero devo cmq poterlo recuperare, dato che a livello fiscale non ci devono essere mancanze, come gestire tutto questo con un sistema web?

Avevo pensato una soluzione del genere.
Esempio:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

Che mi suggerisci?
--------------------
Vincenzo PESANTE
Software Engineer

alx_81 Profilo | Guru

>Il dominio applicativo è quello di un sistema web che genera
>Fatture ed il mio problema è come generare fatture senza incorrere
>in una situazione dove 2 utenti si collegano allo stesso tempo
>rischiando di creare lo stesso numero di fattura?
il numero di fattura lo puoi creare applicativamente (un codice basato anche su una lettura a database). Al momento del salvataggio, che si verifica dopo tot tempo (tempo in cui la fattura viene generata per la conferma immagino), se hai fatto un constraint sul numero fattura (di univocità) ricevi l'errore di violazione del vincolo. In quel caso, lo gestisci e generi un nuovo numero di fattura, ripetendo l'operazione di salvataggio (sempre che tu non voglia invece non salvare o fare altro, deciso da te).

>In piu dopo la creazione del numero devo andare a fare vari insert
>(master-details) dove mi porto dietro appunto tale numero come
>riferimento e se qualcosa andasse male io quel numero devo cmq
>poterlo recuperare, dato che a livello fiscale non ci devono
>essere mancanze, come gestire tutto questo con un sistema web?
non conta se è web.. il salvataggio avviene a server. Se vuoi evitare di avere troppo tempo tra il salvataggio del master e del detail, fai tutto in unica soluzione. Ricapitolando:
- crei un codice di fattura (non usare un identity, perchè un codice di fattura è un codice calcolato ad ogni fattura, l'identità serve per fare surrogati e non ha un vero significato di business)
- crei il master in memory
- crei il detail in memory
- salvi a server entrambi gli oggetti con la seguente logica:
apri la transazione
salvi il master (in questo modo "blocchi" il codice fattura)
salvi il detail
committi la transazione
- se viene violata l'univocità del codice fattura, già il master si arrabbia, e quindi decidi se fare retry oppure dare errore per poi rigenerare un nuovo codice

ma non fare cicli su database. Falli nel server dell'applicazione web, in modo da avere più controllo.

Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

bluland Profilo | Guru


>il numero di fattura lo puoi creare applicativamente (un codice
>basato anche su una lettura a database).
Il numero di fattura deve essere tipo Numeroprogressivo/Anno, per cui devo per forza vedere l'ultimo scritto qual'è o non ho capito bene cosa intendevi?
>

>- crei il master in memory
>- crei il detail in memory
>- salvi a server entrambi gli oggetti con la seguente logica:
Il details di solito è un datatable o cose del genere a livello applicativo, come faccio a passare tutto in un unica transazione?
Significa che la transazione la devo gestire (.net) a livello applicatio e non con SQL Server?


--------------------
Vincenzo PESANTE
Software Engineer

alx_81 Profilo | Guru

>per cui devo per forza vedere l'ultimo scritto qual'è o non ho capito bene cosa intendevi?
che non vanno usate le identity per i progressivi, perchè possono saltare e perchè servono a dare univocità (o surrogato) alle chiavi.

>Il details di solito è un datatable o cose del genere a livello
>applicativo, come faccio a passare tutto in un unica transazione?
>Significa che la transazione la devo gestire (.net) a livello
>applicatio e non con SQL Server?
anche, scegli tu, se fare una stored procedure con la transazione passando tutti i dati anche in tabella, oppure (cosa migliore a mio avviso) fare stored procedure atomiche (che fanno bene solo quello che il loro scope detta) e chiamarle dall'app sotto transactionscope.
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi

bluland Profilo | Guru

Ciao Ale,

Per i progressivi, allora potrei valutare di creare una SP che mi vede il select Max del numero che mi interessa, lo inserisce in una tabella di appoggio, poi i successivi li creo vedendo cosa c'è in tale tabella ed infine prima di chiudere la transazione e di fare l'insert, verifico che non esista gia nella tabella finale, altrimenti faccio il rollback.

Invece per quanto riguarda il passaggio di datatable, mi suggerivi di fare Sp atomiche e gestirle via app con TransactionScope:
(questo significa che non c'è bisogno di creare transazioni a livello SQL se le creo gia a livello applicativo giusto?
In Pseudocode una cosa cosi?
Try Using scope As New TransactionScope() eseguo SP_Master for i =0 to datatable.rows.count -1 eseguo SP_Details next eseguo SP_OtherDetails scope.Complete() End Using Catch ex As Exception Response.Wrinte("Errore: {0}", ex.Message) End Try
Alternativamente se volessi gestire tutto via SQL cosa ne pensi se passo il datable come parametro della SP dichiarando sul Sql server un datype Table?

Certo la prima opzione mi consentirebbe di frammentare meglio le procedure.

Grazie e saluti

Ps
Scusate la lunghezza del post!

--------------------
Vincenzo PESANTE
Software Engineer

alx_81 Profilo | Guru

>Per i progressivi, allora potrei valutare di creare una SP che
>mi vede il select Max del numero che mi interessa, lo inserisce
>in una tabella di appoggio, poi i successivi li creo vedendo
>cosa c'è in tale tabella ed infine prima di chiudere la transazione
>e di fare l'insert, verifico che non esista gia nella tabella finale, altrimenti faccio il rollback.
un trattamento simile all'identity alla fine..

>Invece per quanto riguarda il passaggio di datatable, mi suggerivi
>di fare Sp atomiche e gestirle via app con TransactionScope:
>(questo significa che non c'è bisogno di creare transazioni
>a livello SQL se le creo gia a livello applicativo giusto?
semplicemente, invece che scrivere BEGIN TRAN direttamente nella procedura o nell'sql, stai pilotando il tutto dall'applicazione.

>Alternativamente se volessi gestire tutto via SQL cosa ne pensi
>se passo il datable come parametro della SP dichiarando sul Sql
>server un datype Table?
a livello prestazionale potrebbe essere meglio passare la tabella ad una stored procedure. Fai solo attenzione a non accoppiarti troppo.

>Certo la prima opzione mi consentirebbe di frammentare meglio le procedure.
esatto, e fai valutazioni nel merito del come le useresti. Quello che intendo è:
- se le usi così frammentate, lasciale tali e vedi se puoi ottimizzare il processo anche lato applicativo (sei più modulare)
- se fai una sp cicciotta con tutto, assicurati che essa sia sufficientemente granulare. Altrimenti poi ti tocca spezzarla successivamente.

Per farla breve, se sai che userai sempre la sp che fa sia master che detail, evita di frammentare, che di certo un po' ci perdi in performance (sei costretto a fare cicli, mentre con quella unica, puoi lavorare SET BASED).
Alessandro Alpi | SQL Server MVP
MCP|MCITP|MCTS|MCT

http://blogs.dotnethell.it/suxstellino
http://suxstellino.wordpress.com
http://mvp.microsoft.com/profiles/Alessandro.Alpi
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2025
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5