24 messaggi dal 20 giugno 2005
Ciao a tutti,
ho necessità di aggiornare un record ed essere sicuro che la variazione sia fatta da un solo utente alla volta, quindi vorrei fare il lock e unlock del record nel modo più veloce possibile e senza stressare il db, ho scritto questa soluzione:

BEGIN TRANSACTION
SELECT * FROM PROGRESSIVI WITH (UpdLock) WHERE KEY01 ='CLI';
UPDATE PROGRESSIVI SET PROGRESSIVO = PROGRESSIVO + 1 WHERE KEY01 ='CLI';
COMMIT TRANSACTION

c'è una soluzione migliore.

Vi ringrazio per il supporto.
1.976 messaggi dal 27 luglio 2005
Contributi
salve,

la transazione in se', senza la necessita' di specificare un lock al momento della proiezione iniziale, rende atomica l'operazione...

questo impone solo uno shared lock sulla riga proiettata, ma in ogni caso, nel tuo esempio, non c'e' codice che possa causare delays tra questa proiezione e il successivo comando di update...
e ad essere sincero, la prima istruzione di proiezione e' completamente inutile...

in questo specifico caso, l'aggiornamento di una singola riga (o range di righe), e' sufficiente eseguire il comando di update e non e' necessaria nemmeno la transazione, visto che comunque l'operazione di aggiornamento e' atomica ed autosufficiente...

altro discorso tu usassi ad esempio un cursore per ciclare n righe, ed il ciclo comporti ritardi anche importanti... in questo caso posso comprenderei l'utilizzo espresso transazionale per far si' che la "lettura iniziale" mantenga lo stato sino all'esaurimento del ciclo per garantire che comunque, nel frattempo, non intervengano modifiche da terze parti.
Ma in questo semplice caso, tendenzialmente NON puo' succedere niente... questo, ovviamente, solo lato db...

lato client, invece, il comando inviato a SQL Server viene inlistato nella coda di comandi da eseguire, e nulla vieta che altra connessione modifichi la medesima riga subito prima e/o anche subito dopo la tua modifica, e questo "primo caso" non e' da te qui gestito...
con concorrenza ottimistica, come standard naturale di programmazione con i dbms, il tuo comando NON verifica che tu stia aggiornando la medesima riga che hai letto, questo da intendersi nel contesto della valorizzazione e non nel contesto della chiave, che ovviamente ti aggiorna la riga indicata.
per chiarire... tu hai letto la riga X al momento T0, ed era valorizzata
KEY01 ='CLI'
Progressivo = 1
... Altre colonne...


nel frattempo, un'altra connessione ha modificato la medesima riga, quindi nel momento T1, la valorizzazione sara'
KEY01 ='CLI'
Progressivo = 2
... Altre colonne...(modificate anche quelle, magari)


esegui le tue manipolazioni lato client, ed invii finalmente il comando qui descritto nel momento T2...
la "tua" riga 'CLI" (presupponendo che KEY01 sia una chiave primaria, ma lo stesso discorso vale in maniera piu' ampia se anche fosse un range), nel momento T2, NON sara' piu' valorizzata nello stesso identico modo rispetto al tempo T0.
La tua istruzione specifica, in questo caso, non dovrebbe risultarne penalizzata, in quanto viene fatto "solo" un incremento di valore rispetto al tempo attuale (che ora e' T2 e non piu' T0).
Ma anche in questo semplice caso ci sono circostanze dove potresti anche corrompere la riga, se c'e' logica business di validazione sia semplice (stessa riga) che complessa/incrociata da utilizzare... quindi andrebbe forse usata una concorrenza sempre ottimistica ma piu' stringente/vincolante, con magari utilizzo di rowversion per il controllo del risultato...
Ma forse divago troppo...

se anche KEY01 ='CLI' fosse un range di righe, il semplice comando di aggiornamento di base NON richiede una transazione esplicita in quanto, di nuovo, e' un'azione atomica e SQL Server garantisce le proprieta' ACID al momento dell'esecuzione di DML.

salutoni romagnoli
Modificato da Andrea Montanari il 05 agosto 2021 16:36 -
Modificato da Andrea Montanari il 05 agosto 2021 16:37 -

Andrea Montanari
http://www.hotelsole.com - http://www.hotelsole.com/asql/index.php
24 messaggi dal 20 giugno 2005
Ciao Andrea,
mi hai confermato quello che già sapevo, e sdoppiato gli scenari.

Scenario 1.
Diciamo che quel contatore viene aggiornato da un tasto e non ci sono operazioni intermedie, e se ho interpretato correttamente quello che hai scritto significa che quando premo il tasto di incremento, l'operazione è talmente veloce che se un altro utente preme il tasto incremento andrà in coda e leggerà il mio incremento.


L'altro scenario è se io visualizzo il contattore e l'ho fa anche un altro utente (quindi visualizzano lo stesso dato) e l'altro utente aggiorna, io non saprò mai della variazione se non gestisco la concorrenza ottimistica.

Lavorando con VB.Net e Access, questo controllo me lo faceva in automatico il Dataset e se tentavo di fare una scrittura su un record modificato, mi dava una eccezione e io rileggevo il nuovo record e lo visualizzavo per permettere la nuova variazione.
Mi consigli di fare la stessa cosa?
Comunque la maggior parte delle operazioni saranno sempre come lo scenario 1, anche se i record hanno un numero di campi maggiore di quello che ti ho presentato e posso stare tranquillo per le variazioni. Per quelle situazioni dove l'utente visualizza i dati e lascia la postazione per riprenderli dopo mezz'ora devo vedere come gestirli.

Ti ringrazio per l'aiuto.
Cordialmente
Sebastiano Sassano
1.976 messaggi dal 27 luglio 2005
Contributi
ciao Sebastiano
giari ha scritto:


Scenario 1.
Diciamo che quel contatore viene aggiornato da un tasto e non ci sono operazioni intermedie, e se ho interpretato correttamente quello che hai scritto significa che quando premo il tasto di incremento, l'operazione è talmente veloce che se un altro utente preme il tasto incremento andrà in coda e leggerà il mio incremento.


lo stesso vale per "te", nel senso che se altri hanno gia' fatto "+1", quando tu premi il tasto, il risultato finale sara' "+1 [dell'altro] +1 [tuo]"
e' il classico principio di utilizzo dei dbms...
considera la prenotazione di un biglietto aereo... tu ti loggi a T0, fai le tue "cose", finalmente scegli il posto, e al tempo T3 finalmente spingi <Conferma>...

io entro al tempo T1... l'unico posto disponibile e' ancora libero (tu non l'hai ancora confermato), ed io lo posso vedere... a T2 io confermo...

quando tu in T3 confermi, non ci sara' piu' disponibilita' e non ti restituiscono 404 ma "posti esauriti"... oppure se c'e' ancora disponibilita' e la scelta del posto NON FOSSE individuale ma semplicemente di "1 posto", allora semplicemente ti verra' assegnato questo "1 posto" e la disponibilita' continuera' a diminuire :D


L'altro scenario è se io visualizzo il contattore e l'ho fa anche un altro utente (quindi visualizzano lo stesso dato) e l'altro utente aggiorna, io non saprò mai della variazione se non gestisco la concorrenza ottimistica.


ci sono funzionalita' di lock ottimistico in questo senso, fornite da SQL Server, ad esempio una colonna di tipo timestamp/rowversion, che viene aggiornata autonomamente dal motore al momento di ogni attivita' di aggiornamento, che ti puo' aiutare in questo senso... questo sarebbe il lock ottimistico piu' vincolante... altre forme di lock possono essere piu' "leggere", nel senso di verificare che solo le colonne modificate siano ancora come al tempo T0, quindi
UPDATE ...
SET col1 = nuovo_val,
col5 = nuovo_val
col10 = nuovo_val
WHERE chiavePrimaria = valChiavePrimaria
AND col1 = vecchio_val
AND col5 = vecchio_val
AND col10 = vecchio_val


che come vedi restringono la ricerca di congruita' con T0, mentre l'utilizzo di rowversion e' in questo senso la piu' restrittiva, in quanto indica che tutta la riga debba essere cosi' come al tempo T0
UPDATE ...
SET col1 = nuovo_val,
col5 = nuovo_val
col10 = nuovo_val
WHERE chiavePrimaria = valChiavePrimaria
AND colRowVersion = vecchio_RowVersion



Lavorando con VB.Net e Access, questo controllo me lo faceva in automatico il Dataset e se tentavo di fare una scrittura su un record modificato, mi dava una eccezione e io rileggevo il nuovo record e lo visualizzavo per permettere la nuova variazione.
Mi consigli di fare la stessa cosa?

non mi risulta che il dataset restituisca questo errore in caso di modifica ai dati se non al momento dell'update reale... ci sono impostazioni sulla concorrenza ma al momento non le ricordo e dovrei riguardare il Sheppa, visto che personalmente NON ho mai usato direttamente i metodi di aggiornamento di dataset e datatable, ma ho sempre operato ciclando sulle righe modificate per eseguire stored procedure di insert/update/delete.... questo e' il mio normale modus operandi.

poi ognuno fa le proprie scelte, compatibilmente con lo standard che vuole/deve adottare e come deve risolvere la concorrenza :D


Comunque la maggior parte delle operazioni saranno sempre come lo scenario 1, anche se i record hanno un numero di campi maggiore di quello che ti ho presentato e posso stare tranquillo per le variazioni. Per quelle situazioni dove l'utente visualizza i dati e lascia la postazione per riprenderli dopo mezz'ora devo vedere come gestirli.


anche il caso 1 va tecnicamente gestito, perche' ad esempio il "+1" potrebbe portarti in overflow, sia "fisico" che "logico" (nel senso che i valori validi di co1 sono magari nel range 1 => n <= 100 (e magari anche in "relazione" ad altra colonna della stessa riga o altra entita')...
quindi va comunque gestito
Modificato da Andrea Montanari il 05 agosto 2021 18:28 - dimenticavo....
salutoni romagnoli :)

Andrea Montanari
http://www.hotelsole.com - http://www.hotelsole.com/asql/index.php
24 messaggi dal 20 giugno 2005
Diciamo risolto tutto (è da mettere in codice). Per quanto riguarda il dataset, io utilizzavo l'oggetto del toolbox e quello che hai scritto tu era scritta nell'istruzione di UPDATE, ora la differenza da VB.Net che utilizzavo devo scrivere io quei controlli.
Centrato in pieno l'esempio del biglietto aereo e risolvo tutto implementando il codice che mi hai messo di esempio.
Per lo scenario 1, penso di non essere stato chiaro, io intendevo una pagina web dove c'è un solo tasto, l'utente prima preme e poi legge il numero, questa operazione mettila su "n" computer, e quindi mi chiedo c'è la possibilità di un conflitto o un errore? Per quello che sapevo e dalla tua conferma, le operazioni sono talmente veloci che è sufficiente la gestione delle code di Sql per risolvere il problema.

Di nuovo grazie per il supporto.

Cordialmente
Sebastiano Sassano

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.