Una funzionalita' molto importante, fornita
per altro dalla maggior parte dei DBMS relazionali, e' la gestione delle transazioni
(vedi il documento Gestione delle transazioni sulle basi
dati relazionali).
La gestione completa delle transazioni deve rispettare le proprieta'
ACID (Atomicity, Consistency, Isolation, Durability).
Le tecniche utilizzate sono il data logging, l'utilizzo della before image o
dei rollback segment, il locking dei diversi oggetti, ...
naturalmente con modalita' differenti a seconda del software utilizzato.
Piu' complessa e' la gestione delle transazioni quando queste debbono essere svolte su basi dati distribuite. In questo caso e' necessario l'utilizzo di un particolare protocollo detto Two Phase Commit (2PC). Tale protocollo e' implementato in alcuni sistemi transazionali e, tra gli altri, nell'RDBMS Oracle.
Semplificando molto una transazione e' una serie di modifiche effettuate alla base dati che deve essere eseguita interamente (commit) o per nulla (rollback). Questo deve avvenire in ogni condizione, anche in caso di blocco della base dati, del sistema ospite o di recupero da backup.
Le proprieta' ACID di un sistema transazionale richiedono infatti:
Lo standard ANSI/ISO SQL prevede quattro livelli di isolamento dei dati:
Read uncommitted, Read committed, Repeatable reads, Serializable.
La modalita' di implementazione differiscono molto
e differenti sono le prestazioni e le funzionalita' fornite... ma comunque
tutti i database relazionali implementano in modo completo le transazioni
fornendo le proprieta' ACID e garantendo uno o piu' isolation level
nella gestione dei dati.
La normale gestione delle transazioni
non e' sufficiente quando si effettuano transazioni distribuite. L'applicazione effettua una modifica
sulla base dati A, quindi effettua una modifica sulla base dati B, infine
desidera confermare entrambe le operazioni. Il protocollo 2PC consente la gestione
delle transazioni in ambiente distribuito. Con il protocollo di 2PC il commit dei
dati avviene in due passi. Nel primo passo un "coordinatore"
della transazione manda il messaggio di PREPARE TO
COMMIT a tutte le basi dati o agenti interessati dalla trasazione. Se
tutti rispondono positivamente entro il timeout il coordinatore invia il messaggio
di COMMIT; se qualcuno risponde negativamente
o non risponde, viene inviato il messaggio di ROLLBACK. Con tale protocollo il controllo di una
transazione distribuita e' completo. In qualsiasi situazione e' determinato
univocamente lo stato della transazione. Alcune implicazioni Il traffico di rete che una transazione
in 2PC effettua e' notevolmente maggiore rispetto alle transazioni tradizionali.
Il numero di messaggi e' maggiore e, soprattutto, aumenta la latenza.
Il tempo di timeout deve essere scelto
con attenzione e, generalmente, non e' di breve durata. E' infatti necessario
dare tempo sufficiente a tutti i server interessati di rispondere positivamente
(anche se carichi o connessi con reti lente); altrimenti verrebbe effettuato
il rollback di transazioni che avrebbero potuto essere concluse positivamente. Poiche' il tempo di timeout non e' di
breve durata, in caso di problemi sulla rete e' spesso necessaria una lunga
attesa prima che venga effettuato un ROLLBACK e chiusa la transazione. Se il coordinatore della transazione
cade o se la rete non consente il collegamento tra i vari sistemi che ospitano
le banche dati le transazioni restano in uno stato attesa. La transazione e'
in effetti attiva e verra' risolta sucessivamente. In tale stato, che puo' essere anche
di lunga durata, debbono ovviamente essere mantenuti attivi tutti i lock sulle
risorse impegnate dalla transazione. Questo puo' portare alla necessita' di
"risolvere" manualmente le transazioni attive in modo da liberare
i lock su righe e tabelle. Poiche' tutti i database presenti in
rete vengono coordinati da transazioni in 2PC e' importante mantenere il costante
allineamento delle banche dati. Questo deve avvenire anche nel caso di un ripristino
da backup di un solo database. Tutti gli altri database in rete debbono essere
allineati con la stessa versione dei dati. Nel caso in cui una risorsa utilizzata non sia
attiva ovviamente le transazioni distribuite non possono avere luogo.
Per tale ragione i livelli di servizio delle varie banche dati
ed i servizi coinvolti nelle
transazioni distribuite debbono essere allineati tra loro (eg. backup, attivita'
di manutenzione, ...). Oracle,
dalla versione 7.0 con l'installazione
dell'opzione Distribuite Database Option e con l'utilizzo di SQL*Net 2,
fornisce il protocollo di Two Phase Commit e quindi il supporto completo
delle transazioni in ambiente distribuito.
A parte la corretta installazione dei
pacchetti e delle opzioni non e' necessario effettuare altro. Dal punto di vista
dell'utilizzo le transazioni distribuite ed il protocollo di 2PC sono semplicissimi
e trasparenti. E' sufficiente creare dei database link tra le basi dati e quindi,
anche senza definire sinonimi, e' gia' possibile effettuare transazioni distribuite
semplicemente effettuando operazioni di modifica su tabelle di istanze diverse.
Automaticamente Oracle capisce che si tratta di una transazione distribuita ed
utilizza il protocollo 2PC per coordinare le transazioni tra le diverse istanze
coinvolte. I "coordinatore" e' tipicamente l'istanza da cui si avvia la transazione.
Dal punto di vista tecnico le cose non sono cosi' semplici...
Anche se l'utilizzo del 2PC e' semplice dal punto di vista applicativo,
usare le transazioni distribuite quando non serve e' SBAGLIATO.
Il 2PC e' molto piu' pesante di una transazione locale ed introduce una
serie di possibili rallentamenti e blocchi alle applicazioni.
In alcuni casi particolari puo' avvenire
che le transazioni distribuite non possono essere risolte (eg. il coordinatore
della transazione non e' attivo). Come abbiamo visto
in questo caso il DBA puo' controllare e quindi
risolvere manualmente la transazione.
Alcuni parametri dell'INIT influenzano
la gestione delle transazioni distribuite. Tra i principali: DISTRIBUTED_LOCK_TIMEOUT,
DISTRIBUTED_RECOVERY_CONNECTION_HOLD_TIME, DISTRIBUTED_TRANSACTIONS,
COMMIT_POINT_STRENGTH,
...
L'uso del 2PC richiede una gestione maggiore da parte del DBA e
rende necessario il coordinamento di basi dati diverse...
Va utilizzato solo se necessario!
Nel 1991 X/Open definisce le specifiche
per il Distribuited Transaction Processing
(DTP).
Oracle si uniforma a tali specifiche fornendo un
interfaccia di programmazione: la Oracle XA library.
Un'applicazione XA non utilizza COMMIT, ROLLBACK, statement SQL...
ma deve richiedere servizi agli RM e confermare le transazioni al TM.
Si tratta quindi di un'applicazione "diversa" dalle normali applicazioni
che accedono ad Oracle con le librerie tradizionali.
Oracle supporta il 2PC in modo nativo.
Se acceduto tramite un'interfaccia XA funziona perfettamente o quasi.
Tutte le normali attivita' avvengono correttamente coordinate con
il protocollo di Two Phase Commit.
Ma per soddisfare completamente lo standard servono un paio
di comandi di configurazione,
altrimenti in caso di crash del TM il recovery puo' restituire un errore.
Ecco i comandi necessari che creano la vista V$XATRANS$:
Per il principio del minimo privilegio
i GRANT possono essere anche concessi agli utenti specifici
che fanno uso delle transazioni distribuite...
ma non fa una grande differenza: l'elenco degli XID delle transazioni
in dubbio non e' poi cosi' interessante!
Una nuova giovinezza nell'utilizzo delle interfacce XA
e' stata portata dai driver JDBC. Questi infatti, dalla versione
2 delle specifiche JDBC, prevedono l'implementazione dell'interfaccia XA.
Utilizzando i driver corretti e' possibile utilizzare l'RDBMS Oracle
come componente di una transazione XA.
L'ACID e'
un argomento sui cui si pensava
non ci fosse piu' nulla da dire, perche' riguarda direttamente la consistenza dati ma ...
in realta' ha visto un'importante evoluzione in questi ultimi anni!
Oltre al Two Phase Commit, dal punto di vista pratico, sono stati sviluppati nuovi protocolli come:
il protocollo di consenso Paxos [NdE: 1998] e le sue ottimizzazioni (eg. Raft 2014),
le implementazioni di database NoSQL e Big Data, ...
Dal punto di vista teorico sono stati studiati:
il teorema CAP (Consistency, Availability, Partition tolerance) [NdE: 2000],
la classificazione dei database in PACELC
(on Partition: Availability or Consistency Else: Latency or Consistency),
le BASE transactions
(Basic Availability, Soft-state, Eventual consistency),
...
L'idea di base del teorema CAP, senza entrare in dettagli ne cercare di essere precisi, e':
quando c'e' una partizione tra i nodi che ospitano un database distribuito
non si possono avere contemporaneamente disponibilita' e consistenza senza latenza.
Sempre semplificando molto, la differenza dal protocollo 2-Phase-Commit ed i piu' recenti
protocolli Paxos, Raft, ... e' che anziche' attendere la conferma di tutti i nodi del cluster come nel 2PC
ci si accontenta di una maggioranza.
Sul mercato, oltre ai Database relazionali che garantiscono CA
(Consistency&Availability: Oracle, MySQL, MSSQL, PostgreSQL, Vertica, Kafka, ...),
si sono affiancati in questi anni
Database noSQL che rinunciano alla disponibilita' garantendo CP
(Consistency&Partition Tolerance: HBase, MongoDB, Redis, Google Bigtable/Spanner, HyperTable, Terrastore, YugabyteDB, CockroachDB, ZooKeeper, ...)
e
Database noSQL che rinunciano alla consistenza garantendo AP
(Availability&Partition Tolerance: CouchDB, Cassandra, DynamoDB, Riak, Voldemort, Blockchain networks, ...).
Vi sono poi configurazioni particolari che consentono di fornire una maggior
resilienza ai problemi o che consentono ad prodotto di agire un modo differente.
Ad esempio MySQL e' un normale relazionale ed e' quindi un CA,
la replica MySQL puo' essere configurata in modalita' AP se si lascia scrivere su uno slave,
la soluzione Galera MySQL Cluster e' tipicamente considerata una CP,
Google Spanner tecnicamente e' un CP pero' e' praticamente quasi un CA poiche' si basa su
una rete interna ad altissima affidabilita',
le blockchain networks come quella dei Bitcoin sono AP ma vengono descritte come
come eventually consistent ed in effetti si sono verificati casi di rollback
anche se rarissimi e risolti in poche ore,
...
Argomenti distinti, ma che presentano
aspetti architetturali di interesse, sono: Testo: Il protocollo di Two Phase Commit
Ogni transazione deve essere essere eseguita in modo totale o per nulla; non sono ammesse esecuzioni parziali.
La base dati deve essere coerente: all'inizio ed alla fine delle transazioni tutti i vincoli di integrita'
debbono essere rispettati.
Ogni transazione deve essere eseguita in modo isolato dalle altre.
Terminata una transazione i dati confermati non debbono essere piu' persi.
A questo punto un semplice messaggio di Commit non consente una gestione corretta
della transazione distribuita. Infatti e' possibile che una delle due banche
dati, per una qualsiasi ragione, sia stata posta fuori linea dopo l'avvenuta
modifica dei dati, e l'operazione di commit eseguita sull'altra banca dati porterebbe
ad una situazione inconsistente.
Ogni transazione deve essere confermata da tutti i nodi che partecipano alla
transazione.
In caso di un nodo non disponibile per decidere il rollback della transazione
deve essere atteso un timeot.
Nelle versioni successive non sono state introdotte variazioni
significative... attualmente la DDO fa parte delle funzionalita' di
base della distribuzione Enterprise.
Il "coordinatore" in terminologia Oracle e' chiamato Commit Point
e la scelta, tra i vari nodi che partecipano alla trasazione, e'
basata sul parametro COMMIT_POINT_STRENGHT.
Se i nodi hanno la stessa "forza" viene scelta l'istanza che ha
iniziato la transazione.
Tutte le transazioni in dubbio vengono mantenute sulla vista
DBA_2PC_PENDING che e' controllabile da parte del DBA.
Utile e' anche la vista
DBA_2PC_NEIGHBORS.
Una transazione 2PC puo' rimanere in dubbio a lungo...
naturalmente mantenendo lock sui dati ed occupando redo!
Puo' essere necessario forzare una transazione con il comando
COMMIT FORCE 'trans_ID'; dove il trans_ID e'
quello indicato nella DBA_2PC_PENDING.
Un comando analogo (ROLLBACK FORCE) e' utilizzato per forzare
un rollback.
Sempre a carico del DBA sono alcune configurazioni e l'eventuale tuning...
Per un numero limitato di transazioni i valori di default sono sufficenti
ma con un uso significativo del 2PC vanno monitorati ed eventualmente modificati.
La libreria XA consente di utilizzare l'RDBMS Oracle come
Resource Manager (RM) in transazioni distribuite coordinate
da un Transaction Manager (TM) esterno e richieste da un
Application Program (AP).
Naturalmente tale architettura diventa interessante quando
vengono utilizzati
diversi RM, eventualmente di fornitori differenti,
per realizzare una transazione distribuita
e coordinata.
Le funzioni introdotte dallo standard ed implementate da Oracle sono:
XA Subroutine Descrizione
xa_open()
Si collega al Resource Manager (RM).
xa_close()
Si disconnette dal RM.
xa_start()
Inizia una transazione e la associa ad un transaction ID (XID).
xa_end()
Disassocia il processo dallo XID.
xa_rollback()
Effettua un Rollback della transazione dello XID passato come parametro.
xa_prepare()
Prepara la transazione associata allo XID. E' la prima fase del Two Phase Commit (2PC).
xa_commit()
Effettua il commit della transazione XID. Questa e' la seconda parte del 2PC.
xa_recover()
Restituisce la liste delle transazioni Prepared, Committed o Rolled Back in modo euristico.
xa_forget()
Dimentica la transazione associata allo XID.
Nei servizi richiamati si utilizzano i normali statement SQL
ma naturalmente non possono essere utilizzati DDL (con Oracle forzano un commit
implicito). Inoltre, nel caso in cui vengano acceduti dalla transazione XA
oggetti via DB Link, questi ultimi debbono essere ospitati su un'istanza
con un listener MTS.
start $ORACLE_HOME/rdbms/admin/xaview.sql
grant select on v$xatrans$ to public;
grant select on pending_trans$ to public;
grant select on dba_2pc_pending to public;
grant select on dba_pending_transactions to public;
grant execute on dbms_system to user; (>10g R1)
Rispetto alla normale configurazione JDBC l'unica differenza significativa
e' l'utilizzo della classe DriverClassname:
oracle.jdbc.xa.client.OracleXADataSource,
anziche' della classe
oracle.jdbc.driver.OracleDriver
tutte le altre configurazioni sono identiche (eg. URL:
jdbc:oracle:thin:@<server>[:<1521>]:<database_name>).
La paginetta Drivers JDBC Oracle
descrive con maggiore dettaglio i driver,
le versioni (per disporre dell'interfaccia XA completa occorre
usare l'ojdbc14.jar o successivi) ed il loro utilizzo
nei piu' comuni ambienti J2EE.
Ecco un esempio di configurazione di un
datasource Oracle XA su JBoss:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<xa-datasource>
<jndi-name>XAOracleDS</jndi-name>
<track-connection-by-tx/>
<isSameRM-override-value>false</isSameRM-override-value>
<xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
<xa-datasource-property name="URL">jdbc:oracle:thin:@db.mydomain.it:1569:testdb</xa-datasource-property>
<xa-datasource-property name="User">scott</xa-datasource-property>
<xa-datasource-property name="Password">tiger</xa-datasource-property>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<no-tx-separate-pools/>
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</xa-datasource>
<mbean code="org.jboss.resource.adapter.jdbc.vendor.OracleXAExceptionFormatter"
name="jboss.jca:service=OracleXAExceptionFormatter">
<depends optional-attribute-name="TransactionManagerService">jboss:service=TransactionManager</depends>
</mbean>
</datasources>
Infatti la regola ACID (Atomicity, Consistency, Isolation, Durability)
e' stata messa in profonda discussione dalla nascita di basi dati cosi' grandi
oppure di uso cosi' diffuso
da non poter essere poste su un solo server o su un numero limitato
di server o addirittura su un solo continente.
La suddivisione in PACELC e' un'estesione al teorema CAP che
analizza il comportamento nel caso in cui vi sia un partizionamento
della rete scegliendo tra una maggiore latenza o rinunciando alla consistenza.
Quindi, e questa e' probabilmente la cosa piu' importante,
tutti i database distribuiti hanno molteplici possibilita' di configurazione
che possono variare il loro comportamento nei casi di partizione di rete,
caduta di nodi, costituzione del quorum, conferma delle transazioni,
sovraccarico dei server, errori, ...
la collocazione teorica in una categoria (eg. CP o PA/EL)
non e' sempre fissa e certamente non e' l'unico elemento da considerare
per valutare le caratteristiche di un'architettura.
Data: 17 Settembre 1997
Versione: 1.2.3.4 - 14 Febbraio 2017
Autore: mail@meo.bogliolo.name