Utilizzare le transazioni con ASP


Home Page | Comments | Articles | Faq | Documents | Search | Archive | Tales from the Machine Room | Contribute
A cura di Davide Bianchi L'utilizzo delle transazioni fornisce una maggiore sicurezza nell'apportare modifiche ad un database, inoltre permette di recuperare i dati nel caso in cui un qualche errore impedisca il completamento degli aggiornamenti.

Un pericolo: la scrittura I database sono utilizzati per conservare le informazioni. Elementare. Questo indipendentemente dal tipo di database che si sta' utilizzando.

Che sia un super-sofisticato server con gigabyte di spazio di tabelle, o un volgare file .DBF, lo scopo ultimo e' quello di memorizzare dati in un modo che consenta un facile accesso e reperimento degli stessi.

La lettura dei dati stessi non e' poi un problema, raramente si incontrano degli errori o si creano guai leggendo semplicemente i dati da un database.

Supponiamo che nel bel mezzo di una lettura il computer si blocchi, il peggio che puo' capitare e' che il nostro programma non si accorga di aver letto meta' di quello che gli serve e ci proponga delle informazioni errate ritenendole giuste, ma avviene di rado perche' (di solito) il programmatore prevede la possibilita' che il database si blocchi o risponda in modo anomalo. Le informazioni sono comunque presenti nel nostro database, ed in un modo o nell'altro riusciremo a metterci sopra le mani.

Il momento piu' critico per qualunque sistema di memorizzazione e' la scrittura, cioe' quando le informazioni vengono effettivamente immesse nel sistema.

Se il sistema si blocca per qualunque motivo nel mezzo dell'operazione di scrittura, non vi e' nessuna certezza su cosa e' stato scritto e dove. Un confronto tra lo stato del database prima e dopo ci potra' dire solo che il database e' stato cambiato (per forza), ma difficilmente ci potra' essere d'aiuto.

Questo e' ancora piu' importante quando le operazioni di scrittura devono essere correlate, cioe' e' necessario aggiornare piu' di una tabella in modo coerente.

Faccio il classico esempio: il programma di contabilita' che deve aggiornare le tabelle delle Entrate ed Uscite in risposta all'inserimento di un qualunque movimento contabile.

Se il sistema dovesse bloccarsi a meta' della scrittura cosa occorre fare? E' stata scritta l'entrata? E l'uscita? Se non si ha la sicurezza di quali informazioni sono state scritte e dove, la situazione richiede l'intervento di un tecnico che vada a rimettere a posto le cose a mano. Immaginiamo di avere un sistema che aggiorna una dozzina (o piu') di tabelle ad ogni operazione, si capisce bene che l'intero sistema e' fortemente a rischio.

Le scritture atomiche

Una prima soluzione al problema e' quello di utilizzare operazioni di scrittura 'atomiche', cioe' minime, che vanno a modificare una singola informazione e nient'altro.

A questo punto e' possibile seguire l'operazione di scrittura suddividendola in 'passi', ad ogni passo un solo punto puo' essere causa di errore.

Si ottiene quindi una struttura del tipo:


Inizio operazioni
  Aggiorna tabella1
  Errore?
    Recupera tabella1
   
  Aggiorna tabella2
  Errore?
    Recupera tabella2
    Recupera tabella1
   
  Aggiorna tabella3
  Errore?
    Recupera tabella3
    Recupera tabella2
    Recupera tabella1
   
Fine operazioni
In questo modo e' possibile aggiornare i dati in modo da 'recuperare' eventuali errori. Ma solo fino ad un certo punto, perche' se la funzione di 'recupero' dovesse andare in errore, saremmo al punto di partenza, salvo il fatto che il tecnico saprebbe in quali tabelle occorre andare a mettere le mani.

Insomma la strada e' quella giusta ma ancora non ci siamo.

Scrittura su temporaneo

Supponiamo pero' di unire al meccanismo precedente una scrittura su di una tabella temporanea, in questo modo gli aggiornamenti non sono fatti sulla tabella vera, ma su una copia della stessa che esiste in via temporanea.

A questo punto avremmo:


Inizio operazioni
  Aggiorna tabella1
  Aggiorna tabella2
  ...
   
  Errore (in qualsiasi punto) ?
    Butta via tutto
  Tutto Ok?
    Riporta in definitivo

Fine operazioni
In questo modo tutte le operazioni vengono 'simulate' in modo completo, il sistema richiede tutti i blocchi, la congruenza dei dati e quant'altro e' necessario per fare l'aggiornamento senza farlo, poi, se tutto e' andato bene, tutte le operazioni vengono concluse in un solo colpo.

Questo significa che:

  1. tutte le tabelle sono state aggiornate come dovuto
  2. nessuna tabella e' stata modificata

L'intero processo a questo punto prende il nome di Transazione, in sostanza il concetto della transazione e' quello di riunire in un unico blocco piu' operazioni di cambiamento (update), in modo da eseguirle tutte in un colpo solo.

Ovviamente non tutto e' cosi' pulito. In particolare potrebbe verificarsi un problema nel preciso istante in cui il computer inizia il processo di scrittura.

In effetti non esiste un sistema certo al 100% ed assolutamente esente di difetti, diciamo solo che un meccanismo come questo riduce la possibilita' di errori ai soli errori hardware (se il computer prende fuoco niente vi puo' evitare i guai).

Supporto delle transazioni

Quasi tutti i database 'moderni' supportano a qualche livello le transazioni, in generale un database implementa il meccanismo delle transazioni tenendo un elenco (detto in gergo log) delle operazioni di update da eseguire, al termine delle operazioni, se si sono verificati degli errori, l'intero log della transazione viene ignorato, nel caso in cui tutto sia andato per il verso giusto, i cambiamenti sono memorizzati nel database.

Attenzione: per cambiamento si intende un qualunque cambiamento, anche l'inserimento e la cancellazione sono cambiamenti.

Perche' usare le transazioni

Le transazioni sono un sistema integrato nel database per assicurare la coerenza delle operazioni di aggiornamento dei dati.

Le transazioni evitano problemi di inserimenti anomali ed assicurano che o tutti o nessun cambiamento viene apportato al sistema.

Usare le transazioni

Di solito i database utilizzano un meccanismo di "ogni operazione: una transazione", quindi per ogni singola operazione viene iniziata e terminata una transazione anche se l'utente non lo sa. Per utilizzare le transazioni in modo da raggruppare piu' operazioni e' necessario specificarlo.

L'uso delle transazioni in un database di solito e' indicato dalle parole chiave BEGIN TRANSACTION.

Alcuni database (Oracle, SQL Server, Sybase) consentono di avere piu' transazioni attive contemporaneamente, in questo caso le transazioni vengono identificate con dei nomi. Per semplicita' qui eviteremo di prendere in considerazione delle transazioni multiple.

La transazione viene 'chiusa' in modo positivo (cioe' aggiornando i dati) utilizzando il comando COMMIT, mentre viene annullata (quindi senza aggiornamento) con il comando ROLLBACK.

La struttura di una transazione quindi e' qualche cosa del tipo:


BEGIN TRANSACTION

UPDATE ...
UPDATE ...
INSERT ..

.. ALTRI COMANDI ..

IF ERROR
  ROLLBACK
ELSE
  COMMIT
Un esempio di codice ASP che utilizzi una transazione potrebbe essere il seguente (per brevita' riporto solo la parte di codice relativa alla transazione vera e propria):


<%
   ' aggiornamento del database
   
   ' inizializzo la transazione
   conn.Execute("BEGIN TRANSACTION")
   
   ' eseguo l'aggiornamento
   conn.Execute("UPDATE tabella1 SET campo='valore'")
   conn.Execute("UPDATE tabella2 SET campo='valore'")
   conn.Execute("UPDATE tabella3 SET campo='valore'")
   conn.Execute("DELETE FROM tabella4 WHERE campo='valore'")
   
   ' chiudo la transazione
   conn.Execute("COMMIT")

%>
ASP e le transazioni

Abbiamo stabilito che l'uso delle transazioni e' cosa buona e giusta, rimane da vedere come utilizzarle nel lavoro di tutti i giorni.

Per cominciare occorre stabilire che il supporto delle transazioni non e' fornito da tutti i database, per esempio i database di tipo file-based (ISAM) di vecchia data non comprendono l'utilizzo delle transazioni, questo anche perché non c'e' una 'intelligenza' dietro al database.

Per esempio i datbase .DBF o Btrieve non forniscono supporto alcuno per le transazioni.

I database piu' moderni invece prevedono l'utilizzo delle transazioni, tra i quali Microsoft Access, Microsoft SQL Server, Oracle (tutte le versioni), Sybase e vari altri che adesso non sto' ad indicare.

A questo punto abbiamo due vie per utilizzare le transazioni con questi sistemi: utilizzare il loro supporto interno direttamente usando BEGIN TRANSACTION / COMMIT o l'equivalente fornitoci da ODBC (ADO) per avviare e concludere le transazioni, oppure utilizzare il Microsoft Transaction Server (dove installato) per gestire le transazioni a livello di script.

Utilizzare BeginTrans e RollBackTrans

Se stiamo usando SQL Server o Oracle e sappiamo che andremo ad utilizzare solo questo, la cosa migliore e' quella di programmare direttamente il server creando delle Stored Procedure per effettuare gli aggiornamenti, quindi chiamare tali SP da ASP. In questo modo evitiamo problemi e rendiamo molto piu' efficiente il tutto.

Se vogliamo pero' rendere piu' portabile il nostro codice, non rimane che utilizzare i meccanismi di ADO/ODBC per accedere alle transazioni, tenendo a mente pero' che se il database non supporta le transazioni, il nostro codice diventa inutile, in quanto i metodi BeginTrans e RollBackTrans non fanno assolutamente nulla.

Per utilizzare questi meccanismi occorre includere le operazioni da rendere transazionali all'interno delle istruzioni:


Connection.BeginTrans
...
Istruzioni di modifica del database
...
Connection.CommitTrans
Il grosso problema di ASP e' sapere quando e' il caso di fare un Rollback, cioe' rendersi conto se si e' verificato o meno un errore durante la scrittura dei dati. Effettivamente quello che manca ad ASP e' una buona gestione degli errori sul tipo di Visual Basic o di altri linguaggi (gestione delle exception per esempio), purtroppo l'unica soluzione e' mettere un bel On Error Resume Next all'inizio del codice e poi testare a mano ogni volta se la connessione ha riportato degli errori, verificando se Connection.Errors.Count e' maggiore di 0.

Facciamo un esempio pratico: abbiamo una paginetta che visualizza i dati da una tabella di database e chiede i dati per aggiungere un record nella stessa tabella. Durante l'aggiunta del record vogliamo anche aggiornare una data su tutti i record alla data odierna.

Fare click per vedere o copiare il codice dell'esempio.

In sostanza abbiamo due 'aree' distinte: la parte superiore visualizza la maschera di inserimento dei dati, la parte inferiore mostra i dati gia' presenti.

La parte 'clue' del codice e' quella indicata come aggiornamento dati. In pratica che si fa: si attiva la transazione richiamando conn.BeginTrans (dove conn e' la mia connessione), si eseguono gli aggiornamenti, quindi si verifica se ci sono stati degli errori, questa e' la parte rognosa, in quanto l'unico modo per farlo e' quello di testare se conn.Errors.Count e' maggiore di zero.

Per poter fare questo pero' e' necessario aggiungere l'istruzione On Error Resume Next, per evitare che il codice si blocchi quando riceve un errore dalla connessione. Se si sono verificati degli errori, viene annullata la transazione (mediante Rollback), altrimenti si effettua la commit e tutto va' a posto.

Testare il codice Per testare questo codice vi occorre un Web Server che supporti ASP, un database minimo, anche Access va' bene, con una tabella denominata DocumentiAsp che contenga i seguenti campi:

Documento testo chiave primaria della tabella
Descrizione testo descrizione qualsiasi
Titolo testo titolo qualsiasi
Data data data qualsiasi

Io ho chiamato la connessione AspSamples, ma potete usare un qualunque altro nome, basta che modifichiate il richiamo all'inizio della pagina.

Come funziona la cosa
Allora, immettete qualunque cosa come Documento ed una data valida come Data, l'aggiornamento cerca di inserire il record nella tabella ed aggiorna tutti i record alla data immessa. Se si commette un errore (per esempio si immette due volte lo stesso documento o si inserisce una data errata), si riceve un messaggio di errore (che non comporta il blocco dello script) e nessuna modifica viene effettuata.

Notare che l'inserimento del record e' indipendente dalla data immessa, infatti l'istruzione SQL usata non comprende la data, l'aggiornamento dei record a sua volta ignora l'inserimento. Sono due operazioni distinte che non hanno niente in comune, ma se una delle due va' male, nessuna delle due e' terminata.

Utilizzare il Microsoft Transaction Server Il Microsoft Transaction Server (d'ora in poi MTS), e' un componente facente parte dell'Option Pack di Windows NT, che puo' essere installato per fornire un supporto transazionale esterno a varie applicazioni che ne fanno uso.

Con questo intendo dire che e' un componente che puo' essere presente, oppure no, quindi prima di cominciare ad usarlo assicuratevi che sia installato.

MTS consente ad una applicazione di accedere ai dati di un database aggiungendo un ulteriore 'strato' di isolamento, in particolare si occupa di fornire il supporto per un accesso di tipo transazionale a tutti i dati del database stesso. MTS consente l'accesso solo a database, non fornisce (purtroppo) supporto per l'accesso ai file o cose simili.

Utilizzando MTS non dobbiamo fare altro che dichiarare che la nostra pagina ASP utilizza una transazione, a questo punto si occupera' MTS di gestirsi la transazione, noi non dovremo piu' preoccuparci della cosa, salvo il fornire ad MTS i sufficienti comandi.

Una transazione MTS deve iniziare e finire in una sola pagina ASP. Non e' possibile distribuire la transazione su piu' pagine. Se i dati provengono da diversi 'oggetti' distribuiti su piu' pagine occorre raccogliere tutte le informazioni in un'unica pagina, quindi iniziare e concludere la transazione su quella pagina.

Dichiarare una pagina 'transazionale' Per utilizzare MTS occorre dichiarare che la nostra pagina e' 'transazionale', nel momento in cui lo facciamo MTS si occupera' di iniziare la transazione e di stabilire se la stessa e' andata a buon fine (COMMIT) o se e' fallita (ROLLBACK).

Per dichiarare la pagina 'transazionale' e' sufficiente apporre il comando:


<%@ TRANSACTION = Required %>
All'inizio della pagina, prima di qualunque altra cosa.

La transazione viene conclusa quando lo script ASP termina, non esiste un comando di chiusura della transazione stessa.

Nel caso in cui la transazione non vada a buon fine, la stessa viene annullata e le modifiche vengono perse.

Attenzione: MTS opera solo su database, non vengono considerate modifiche apportate a variabili di sessione, variabili di applicazione e cose cosi'. E' comunque possibile scrivere delle procedure che ripristinano i valori di variabili in caso di errore di transazione.

Verificare l'esito della transazione
Non esiste uno 'stato' della transazione che possa essere controllato, in realta' quando la transazione viene conclusa, viene chiamata la procedura OnTransactionCommit(), mentre se la stessa fallisce viene richiamata la OnTransactionAbort().


<%@ TRANSACTION = Required %>
<% 
  ' Attivo il buffering, cosi' diverse pagine possono essere visualizzate
  Response.Buffer = True
%>



Benvenuti alla banca del giaguaro

<% ' avvio un componente che utilizza la transazione Set BankAction = Server.CreateObject("MyExample.BankComponent") BankAction.Deposit(Request("AcctNum")) %>

Grazie, stiamo controllando il vostro conto.

<% ' Visualizza questa pagina se tutto funziona. Sub OnTransactionCommit() Response.Write "" Response.Write "" Response.Write "Tutto ok." Response.Write "" Response.Write "" Response.Flush() End Sub %> <% ' Visualizza questa pagina se le cose non funzionano. Sub OnTransactionAbort() Response.Clear() Response.Write "" Response.Write "" Response.Write "Errore." Response.Write "" Response.Write "" Response.Flush() End Sub %>

Attivare MTS
Per poter utilizzare MTS e' necessario che lo stesso sia installato sul server, inoltre occorre che gli oggetti che utilizziamo e che vogliamo sfruttare con le transazioni siano a loro volta registrati in modo che richiedano le transazioni direttamente ad MTS, questo si fa utilizzando l'apposita interfaccia di MTS Explorer.


Comments

Max length of comments: 1000 chars.

nessun commento.

Add a comment (max 1000 chars)

Comment from:
Comment:


Author Davide Bianchi, works as Unix/Linux administrator for a "network security" company of Haarlem.
Contacts: mail: davide AT onlyforfun.net , ICQ: 268751033, Jabber: davideyeahsure AT gmail.com Skype: davideyahsure

Contribuire Volete contribuire? Leggete come!

Copyright This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.

This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.

This site isn't optimized for vision with any specific browser, nor it requires special fonts or resolution.
You're free to see it as you wish.
Web Interoperability Pleadge is this a valid html document?

Last Update: 04/12/2008