Introduzione
Questo
articolo parla in maniera approfondita del sistema transazionale
di storage di Slide. Attraverso esempi di codice reale,
si potrà vedere come una semplice cache transazionale
possa funzionare e quali problematiche debbano essere
affrontate. Sulla base di questo, si imparerà
come i lock sono utilizzati nel file system transazionale
di Slide per ottenere transazioni ACID complete.
Questo
articolo può essere interessante per tutti coloro
che lavorano o che si occupano di transazioni. Può
rappresentare inoltre un breve tutorial su come creare
da soli sistemi transazionali e un'introduzione su come
questo è fatto in Slide.
Cos'è Slide?
Slide
è un content repository compatibile con WebDAV
ospitato dal progetto Jakarta della fondazione del software
di Apache. Attraverso Slide si può immagazzinare
e richiamare qualsiasi risorsa di contenuto, aggiungere
meta dati, mantenere versioni multiple, avere il pieno
controllo di accesso e di ricerca su tutto il testo
così come sui meta dati. WebDAV estende il protocollo
HTTP. Ai ben noti metodi di HTTP, GET, POST e PUT, WebDAV
aggiunge metodi come PROPFIND, SEARCH, LOCK, CHECKIN.
Slide include anche un client software e un vasto insieme
di strumenti per il test approfondito, che non saranno
trattati in questo articolo.
Utilizzando
il protocollo di WebDAV, Slide può essere utilizzato
come un sistema autonomo che controlla i contenuti e
i meta dati come un sistema di database relazionale
in modo del tutto simile a come Oracle gestisce i dati
convenzionali. Per raggiungere questo obiettivo è
sufficiente scaricare Slide e lanciarlo. Un'altra applicazione
di utilizzo di Slide potrebbe consistere nell'includerlo
nel proprio software utilizzando la sua interfaccia
nativa Java (Java API) che supporta le stesse operazioni
dello strato di WebDAV. Se i dati con cui si lavora
possono essere visti come contenuto, con Slide si ha
a disposizione uno strato di persistenza pronto che
può persino funzionare anche in assenza di una
base di dati relazionale.
L'architettura
server di Slide consiste di un nucleo responsabile del
locking, del versioning, dell'autenticazione e della
sicurezza, della ricerca, delle transazioni e di altri
compiti connessi. Al di sopra di questo nucleo è
sviluppato lo strato di WebDAV che fornisce i metodi
supplementari all'HTTP così come sono definiti
negli standard WebDAV, ACL, DeltaV e DASL.
Il nucleo stesso utilizza un livello di persistenza
a due livelli altamente flessibile. Utilizzando i file
di configurazione, le risorse identificate da URI gerarchici
possono essere mappate in depositi logici composti.
Questi depositi logici possono poi delegare funzioni
differenti di una risorsa a diversi depositi fisici.
Avere
lo storage così organizzato può sembrare
complicato, ma permette di realizzare alcune configurazioni
utili. Si potrebbe, per esempio, memorizzare i documenti
XML in un determinato folder mappato in una base di
dati dedicata, mentre i documenti Word in un folder
differente mappato direttamente sul file system. Si
potrebbe anche per esempio immagazzinare tutti i contenuti
nel file system e tutti i meta dati in un database relazionale.
Poiché Slide possiede già le implementazioni
per depositi fisici per file system transazionali, basi
di dati relazionali e database Tamino XML, è
possibile utilizzare direttamente tutte queste configurazioni.
Con
questo articolo si vuole fare luce su come una cache
di dati possa venire utilizzata al primo livello e su
come il file system relazionale di Slide possa essere
impiegato come deposito fisico al secondo livello.
Caching transazionale
Come
è comunemente noto accedere ai dati memorizzati
in un deposito persistente è una operazione che
a tendere risulta essere piuttosto lenta. A causa della
separazione in livelli di Slide, il livello WebDAV e
il nucleo generalmente accedono al livello storage più
volte all'interno della stessa richiesta qualche volta
anche richiedendo gli stessi dati. Questo purtroppo
rallenta Slide notevolmente.
Qual
è la soluzione? Caching. Quando pensate ad una
cache, quali caratteristiche richiedereste? Penso che
la maggior parte di voi sarebbero d'accordo con me nel
riconoscere almeno questi requisiti:
- Se
qualcosa è inserito in una cache è desiderabile
mantenerlo accessibile più a lungo possibile
- Se
un entry è trovata, deve essere valida
- La
cache non deve crescere oltre i limiti definiti
- La
cache non dovrebbe mai bloccare
- La
cache dovrebbe essere significativamente più
veloce dell'entità che nasconde
Anche
se questi requisiti sembrano essere chiari a prima vista
e non troppo difficili da realizzare, in realtà
le cose non sono mai così semplici. Il problema
principale è la necessità di mantenere
il supporto transazionale di Slide a tutti gli strati:
non solo a livello dello storage secondario ma anche
all'interno della cache. Ovviamente, delle ben note
proprietà delle transazioni (ACID), la durabilità
non si applica alla cache, anche se questa non deve
impedire allo storage sottostante di perseguire la proprietà
di durabilità.
Vedendolo in questo modo, dovrebbe essere chiaro che
la validità dei dati inseriti costituisce realmente
una problematica. Questo perchè ci potrebbero
essere delle entry sulle quali deve essere fatto roll
back al termine di una transazione oppure casi in cui
l'isolamento dei dati potrebbe essere un problema. Quando
una transazione scrive dei dati in cache e successivamente
li legge, dovrebbe ottenere lo stesso valore, sia dalla
cache sia direttamente dallo strato di persistenza.
È inoltre desiderabile che i cambiamenti fatti
da una delle diverse transazioni simultanee siano invisibili
alle altre. Poichè la velocità rappresenta
la motivazione principale per introdurre una cache,
dovrebbe essere chiaro che le relative prestazioni rappresentano
una problematica da affrontare. Questo ci spiega perchè
è altamente desiderabile non bloccarsi mai o
rimanere in attesa di un particolare evento. Da tenere
presente inoltre un altra importante regolare valida
nel campo della progettazione del software che impone
di mantenere sempre le cose il più semplice possibile:
keep it sample, stupid.
Slide
offre una soluzione parziale a tutti i requisiti della
cache ideale. La sua implementazione di cache transazionale
è basata sulla classe LRUMap della Jakarta Commons
Collections, e che determina la strategia di caching:
least-recently-used. Questo significa che quando la
cache è piena, verrà rimossa l'entry usata
meno di recente. Oltre a questo programma, che serve
come cache globale condivisa da tutte le transazioni,
ci sono inoltre un certo numero di cache che sono riservate
ad ogni transazione. Queste cache sono utilizzate localmente
per memorizzare le informazioni relativamente ai cambiamenti,
alle aggiunte o alle rimozioni dei dati fino a che non
è stato fatto commit o roll back della transazione.
Ci
sono due tipi di cache all'interno di Slide, una per
il contenuto che immagazzina byte e un'altra per la
memorizzazione di meta dati. La cache per i dati è
troppo complessa e per certi versi perfino bizzarra,
quindi ci limiteremo alla cache relativa ai meta dati.
Poiché questa cache non fa distinzione fra aggiornamenti
o aggiunte sui dati, i campi protetti della classe di
caching potrebbero essere qualcosa del tipo:
protected
Map globalCache = new LRUMap(globalCacheSize);
protected Map txChangeCaches = new HashMap();
protected Map txDeleteCaches = new HashMap();
Ogni
volta che viene iniziata una nuova transazione, vengono
create ed associate al mapping opportuno una HashMap
ed una HashSet, in modo da memorizzare rispettivamente
le entry modificate e le chiavi di quelle cancellate.
Questo produce rispettivamente una mappa di mappe ed
una mappa di insiemi. Il codice per iniziare una nuova
transazione potrebbe essere ad esempio qualcosa del
tipo:
public
synchronized void start(Object txId) {
txChangeCaches.put(txId, new HashMap());
txDeleteCaches.put(txId, new HashSet());
}
Come
quasi tutti i metodi della cache c'è bisogno
dell'identificativo di transazione per assegnare all'azione
la transazione relativa. Si può inoltre osservare
che la dimensione della cache dei cambiamenti non è
limitata. Questa è ovviamente una violazione
di una delle proprietà che ci si aspetterebbe
da una cache. Ci sono certamente dei modi per avere
la cache limitata in dimensioni, ma questo comprometterebbe
il principio keep it simple, stupid. Poichè maneggiamo
solamente i meta dati, e non byte di contenuto, si può
rilassare questo vincolo. Vediamo ora che cosa succede
quando aggiungiamo una entry all'interno di una transazione:
public
synchronized void put(Object txId, Object key, Object
value) {
Map changeCache = (Map) txChangeCaches.get(txId);
changeCache.put(key, value);
}
Questo
è semplice e lineare. Invece di immagazzinare
l'entry nella cache globale la si aggiunge a quella
locale e provvisoria. In questo modo si nasconde il
cambiamento sui dati rimandando ogni decisione a dopo.
Il codice semplificato per il recupero all'interno di
una transazione assomiglia a questo:
public
synchronized Object get(Object txId, Object key) {
Map changeCache = (Map) txChangeCaches.get(txId);
Object changed = changeCache.get(key);
if (changed != null) {
return changed;
} else {
Object global = globalCache.get(key);
return global;
}
}
Come
potete vedere la cache locale è controllata per
prima in modo da riflettere i cambiamenti locali e se
non ce ne sono è consultata la cache globale.
Il valore di ritorno null indica che non c'è
una entry valida e i dati devono essere ricercati nel
database. Infine, al termine della transazione, si potrà
effettuare il commit oppure il rollback. Facendo commit,
tutti i cambiamenti locali sono propagati sulla cache
globale, mentre con una rollback i cambiamenti saranno
scartati. Questo è il codice per eseguire il
commit:
public
synchronized void commit(Object txId) {
Map changeCache = (Map) txChangeCaches.get(txId);
for (Iterator it = changeCache.entrySet().iterator();
it.hasNext();) {
Map.Entry entry = (Map.Entry)
it.next();
globalCache.put(entry.getKey(),
entry.getValue());
}
}
Omettiamo
il codice per il rollback dato che risulta intuitivo
come operare per procedere alla rimozione della cache
locale associata alla transazione.
In
questi brevi pezzi di codice si sono riportare le linee
essenziali del sistema: anche se in realtà le
cose sono un po' più complesse in quanto è
necessario gestire la cancellazione dei dati, mantenere
il conteggio degli accessi e il logging. In concetti
essenziali dovrebbero essere comunque piuttosto chiari.
Troppo
semplice in effetti! Il punto fondamentale risiede in
che cosa il metodo get e commit fanno. È importante
notare che facendo commit tutti i cambiamenti locali
sono semplicemente copiati sulla cache globale. Ora
consideriamo due transazioni eseguite in parallelo che
accedono allo stesso dato: una in lettura una in scrittura:
la prima accede alla cache globale mentre le copie locali
sono generate soltanto al momento della scrittura. Quindi,
quando questa transazione legge dalla cache globale
prima del commit e poi dopo il commit della scrittura
otterrà due valori differenti. Questo scenario
è conosciuto come lettura non ripetibile (Nonrepeatable
Read).
C'è
inoltre una violazione che è molto più
severa. Immaginiamo che lo stesso valore sia prima letto
e successivamente scritto in modo dipendente dal valore
letto da due transazioni eseguite in modo concorrente.
Ad esempio, immaginiamo un documento che è prima
letto, poi modificato e quindi scritto da due transazioni
concorrenti. Con la nostra strategia di caching uno
degli aggiornamenti del documento andrà perso.
Questo scenario è conosciuto invece come il problema
dell'aggiornamento perso (Lost Update). Come si comporta
il sistema di cache in merito a questo problema? Semplicemente
niente!
Questo perché la cache in esame non ha un comportamento
ottimale in termini di isolamento! In realtà,
questo non è un problema, dato che lo scenario
appena descritto non può mai verificarsi in Slide.
La scrittura e la lettura dovrebbero costituire distinte
richieste WEBDAV. Semplicemente non sono permesse le
transazioni che comprendono più di una singola
richiesta. Per garantire la correttezza WebDAV fornisce
i metodi LOCK ed UNLOCK che permettono di bloccare l'intero
documento per il periodo dell'aggiornamento.
È
previsto, in Slide, che vengano aggiunte le funzionalità
per gestire transazioni che comprendono più di
una singola richiesta. Per questo scopo il sistema di
caching esistente è semplicemente non sufficiente.
A causa di questo, è in fase di sviluppo un nuovo
sistema di caching che prevede l'utilizzo di lock non
bloccanti e di un sofisticato meccanismo che fornisce
schemi di esecuzione serializzabile.
Il file system transazionale di Slide
Consideriamo
ora il file system transazionale di Slide. Il suo obiettivo
è quello di realizzare l'implementazione di default
dello storage di Slide, in modo da permettergli di funzionare
in assenza o congiuntamente ad un database relazionale.
Prima di esaminare a fondo i dettagli implementativi,
ricapitoliamo alcuni argomenti di cui abbiamo già
discusso.
Il
sistema di caching utilizzato in Slide è abbastanza
debole in termini di isolamento. Verrebbe da chiedersi
il motivo di questa scelta progettuale: la risposta
è per il semplice fatto che il sistema non ha
lock e i lock sono richiesti per impedire violazioni
all'isolamento e alla correttezza. Il file system transazionale
di Slide, pur utilizzando una strategia dei cambiamenti
locali molto simile a quella descritta nel sistema di
caching, ha come caratteristica supplementare i lock.
Utilizzando questi lock in una modalità a due
fasi, il sistema fornisce realmente il livello di isolamento
"serializzabile".
Quali
altre cose dobbiamo considerare quando desideriamo avere
un file system transazionale? Diamo un'occhiata in dettaglio
alle proprietà ACID:
- Atomicità
- Consistenza
- Isolamento
- Durabilità
L'Atomicità
può essere risolta attraverso un meccanismo di
copie locali come introdotto nella sezione del caching.
L'isolamento e la consistenza sono ora garantiti dal
lock a due fasi poichè ci sono dimostrazioni
che evidenziano come il lock a due fasi garantisce un
programma serializzabile. Così la durabilità
rimane l'ultimo nostro problema, giusto? Potreste dire
che la durabilità non rappresenta un problema
poiché è già provvista dal file
system stesso! Questo è vero, ma insieme alla
proprietà d'atomicità, può invece
trasformarsi in un problema.
Per
illustrare l'esistenza reale del problema, consideriamo
come si comporta la cache quando si fa commit di una
transazione. Semplicemente copia tutte le entrate localmente
modificate nella cache globale. Questo è atomico?
Sì è atomico, poiché l'intero commit
è avvolto in un blocco sincronizzato. Cosa accade
se un'interruzione di corrente avviene durante il commit?
Nella peggiore delle ipotesi, la cache è perduta,
ma questo non è un problema perchè la
cache rappresenta un dispositivo volatile! Che cosa
accadrebbe se esportassimo questa pratica dal sistema
di caching al nostro file system? Al momento del commit,
sposteremmo i file memorizzati nelle directory temporanee
locali nelle loro locazioni principali. Di nuovo, che
cosa accadrebbe se un'interruzione di corrente avvenisse
nel bel mezzo dell'operazione? In questo caso sarebbe
un grande problema, infatti dopo il riavvio solamente
una parte dei file sarebbe stata copiata. Questo violerebbe
sicuramente l'atomicità e molto probabilmente
la consistenza. Cosa succederebbe invece nel caso opposto,
ovvero, realizzare una politica che apporta le modifiche
ai file principali e mantenere le copie di backup nelle
directory locali e temporanee? Potremmo farlo perché
ora disponiamo dei lock che potrebbero respingere l'accesso
a quei file principali interessati in una transazione
attiva. Questo risolverebbe il nostro problema? No,
perché in caso di rollback dovremmo copiare quei
backup nelle directory principali e anche in questo
caso l'operazione potrebbe fallire a metà.
Ora
che abbiamo capito il problema, consideriamo la soluzione
per il locking e la durabilità nel file system
transazionale di Slide.
Locking
Il file system transazionale di Slide conosce quattro
differenti tipologie di lock denominate ACCESS, SHARED,
EXCLUSIVE e COMMIT. Concentriamoci su SHARED e su EXCLUSIVE
poiché sono sufficienti per realizzare un locking
corretto a due fasi ACCESS e COMMT sono utili solamente
quando sono richiesti livelli più bassi di isolamento.
Ogni
volta che una risorsa è acceduta in lettura tenta
di acquisire un lock condiviso (shared). Questo lock
può coesistere con altri lock condivisi sulla
stessa risorsa, ma non con uno esclusivo.
Se un lock esclusivo su una risorsa è già
stato ottenuto da un'altra transazione, una transazione
che cerca di accedere alla risorsa dovrà attendere
fintanto che il lock esclusivo non è rilasciato.
È inoltre vero anche nel caso contrario. Se una
transazione prova ad acquisire un lock esclusivo su
una risorsa che ha già un lock condiviso, deve
attendere allo stesso modo. Un lock esclusivo è
necessario quando una risorsa è acceduta in scrittura.
Naturalmente un lock esclusivo non può coesistere
con altri lock dello stesso tipo.
Bene,
fondamentalmente i lock sono questo. L'unica problematica
rimasta aperta è cosa fare se non si può
ottenere un lock, cosa che può accadere ad esempio
quando una transazione si "dimentica" di liberare
i propri lock senza mai fare commit o rollback o quando
due transazioni attendono reciprocamente l'altra per
rilasciare i lock. Il primo scenario è chiamato
livelock, mentre il secondo deadlock. Il primo lock
si definisce "vivo" poichè la transazione
potrebbe risolvere questo scenario senza aiuti dall'esterno:
la transazione potrebbe infatti semplicemente fare commit
o rollback. Nel secondo scenario, invece, è impossibile
per le transazioni fare qualcosa poichè sono
reciprocamente bloccate in attesa l'una dell'altra.
Una
soluzione per il livelock sono i timeout. La soluzione
migliore consisterebbe nel fare il rollback di una transazione
dopo un determinato ammontare di tempo che potrebbe
persino essere modificato dalla transazione stessa.
Per semplicità la nostra implementazione non
ha dei thread di controllo attivi, ma può solamente
influenzare le transazioni quando si effettuano le chiamate
al sistema. Purtroppo, se una transazione non fa nulla
non c'è modo d'arrestarla e di fargli liberare
le risorse e i lock. Questo è un caso sfortunato,
ma quello che si può fare è di effettuare
il rollback delle transazioni che tentano di ottenere
un lock. Questo sarebbe in realtà di poca utilità
se ci fossero delle transazioni in Slide che non fanno
niente. Per fortuna questo scenario non è previsto
in Slide, poiché le transazioni non possono essere
raggiunte o influenzate dalle richieste di WebDAV. Come
già accennato nella sezione del caching ci sono
programmi per controllare le transazioni indipendentemente
da WebDAV. In questo caso i time out attivi sarebbero
indispensabili.
Se
questa modalità per risolvere i live lock non
è mai utilizzata, perchè allora è
stata spiegata in modo così dettagliato? Da un
lato, per evidenziare una limitazione dell'implementazione,
dall'altro perchè questo meccanismo rappresenta
un facile modo per risolvere i deadlock! Il modo più
elegante per individuare deadlock sarebbe quello di
analizzare quali transazioni dipendono da quali lock
ogni volta che una transazione tenta di ottenerne uno
nuovo. Se si trova un ciclo in questo grafico delle
dipendenze, si dovrebbe effettuare il rollback dell'ultima
transazione come vittima del dead lock. La nostra implementazione
non realizza questo tipo di analisi, ma semplicemente
assume che si sia verificato un dead lock dopo un attesa
di un determinato ammontare di tempo. Questo rappresenta
sicuramente un punto che deve essere richiamato e corretto
nei rilasci futuri.
Durabilità
Per
memorizzare tutte le informazioni relative ad una transazione
è stata introdotta la classe TransactionContext.
Questa classe, tra le altre informazioni secondarie
di manutenzione, incapsula:
- l'identificativo
della transazione
- lo
stato della transazione
- la
lista dei lock ottenuti dalla transazione
- la
lista delle risorse utilizzate dalla transazione (es.
stream)
Per
avere un'idea di come il file system transazionale di
Slide risolve il problema della durabilità descritto
precedentemente, è sufficiente sapere che alcune
delle informazioni appena fornite sono rese persistenti.
Le informazioni persistenti più importanti sono
l'identificativo, lo stato della transazione e l'indicazione
di quali file sono stati modificati. Essendo queste
informazioni persistenti si ha la garanzia di poter
sempre recuperare lo stato di una transazione che potrebbe
essere stata interrotta nel mezzo da qualche cosa. Per
illustrarlo, è qui riportato il codice semplificato
per fare commit.
public
void commitTransaction(Object txId) {
TransactionContext context = getContent(txId);
context.status = STATUS_COMMITTING;
context.saveState();
context.commit();
context.status = STATUS_COMMITTED;
context.saveState();
context.cleanUp();
}
Come
nel codice di caching, si riceve l'id della transazione
come parametro e lo si utilizza per ricavare il contesto
transazionale. Ora posiamo vedere qual è il trucco.
In primo luogo, settiamo lo stato a "committing"
e rendiamo questa informazione persistente attraverso
il metodo saveState(). Dopo che si è iniziato
il processo di commit si copiano tutti i file modificati
dalla directory temporanea locale alla locazione principale:
public
synchronized void commit() {
closeResources();
upgradeLockToCommit();
moveRecursive(new File(localChangeDir),
new File(mainDir));
freeLocks();
}
In
primo luogo, tutte le risorse coinvolte nella nostra
transazione vengono chiuse, in quanto viene preso in
considerazione il caso in cui l'utente si sia dimenticato
di farlo. Successivamente, tutti i lock mantenuti dalla
transazione sono aggiornati a lock di commit, il che
significa che nessun altra operazione simultanea è
permessa. Infine tutti i file modificati sono copiati
nella directory principale e tutti i lock vengono rilasciati.
Questi passi illustrano il rigoroso meccanismo di lock
a due fasi qui utilizzato. Non bisogna quindi mai rilasciare
alcun lock o assegnarvi minore importanza, ma solamente
acquisirne dei nuovi o aggiornarli finchè non
si fa commit dell'intera transazione. Al termine del
commit si liberano tutti i lock.
Torniamo
di nuovo al codice di commitTransaction. Dopo aver eseguito
il commit si setta lo stato della transazione a "committed"
e di nuovo si rende questa informazione ancora persistente.
Per concludere, si eseguono alcune operazioni che principalmente
vogliono cancellare i file e le directory temporanee.
La
parte più importante del codice che è
stata omessa è relativa alla cattura e alla gestione
delle eccezioni: in caso di eccezioni il sistema viene
messo in modalità dirty in quanto richiede una
fase di recupero prima che possa gestire nuove richieste.
La fase di recupero può essere invocata manualmente,
ma può anche essere eseguita dal sistema al momento
dell'avvio.
Se
accade un qualche evento veramente grave durante l'operazione
di commit (come l'interruzione della corrente, un errore
relativo alla virtual machine, l'esaurimento della memoria,
etc.) cosa può avvenire al nostro sistema? Ci
sono tre casi da considerare a seconda del momento in
cui l'evento accade:
-
Prima che il processo di commit sia iniziato e che
lo STATUS_COMMITTING sia stato scritto su disco
- Dopo
questo, ma prima che lo STATUS_COMMITTED sia stato
scritto
-
Dopo che lo STATUS_COMMITTED è stato scritto
-
Dopo che il processo di commit sia concluso.
Ovviamente,
nei casi 1 e 4 non ci sono problemi poichè la
transazione ha eseguito il commit completamente o per
nulla. Il caso numero 3 invece non è così
problematico dato l'unica cosa che manca è la
cancellazione dei file temporanei. Non c'è infatti
nessun pericolo relativamente alla consistenza poichè
il meccanismo di recupero cancellerà semplicemente
quei file quando troverà una transazione che
ha già raggiunto lo stato di commit ma che ha
ancora dei file associati.
Rimane quindi il caso 2. Questo rappresenta realmente
un problema, poichè alcuni file possono essere
già stati copiati ed altri no. In questo caso
è importante ricordarsi che nessuna richiesta
può essere gestita quando il sistema si trova
nella modalità dirty o in quella di recupero.
Ciò significa che dall'esterno non è possibile
accedere a dati inconsistenti. La cosa positiva è
che, in qualsiasi momento l'operazione sia stata interrotta,
ci sono sempre abbastanza informazioni per risolvere
questa temporanea inconsistenza di stato. Per di più,
il processo di recupero è così a singolo
thread e quindi sequenziale che nessuna problematica
relativa alla concorrenza deve essere gestita. Ciò
è possibile perchè non dobbiamo fare lock
sulle informazioni che non sono rese persistenti.
Quando
il processo di recupero trova una transazione che ha
lo stato settato a "committing" la esegue
nuovamente (roll forward), cioè copia tutti i
file non ancora copiati finchè non raggiunge
uno stato consistente. Questo è possibile poichè
tutti i file già copiati sono successivamente
cancellati. Dopo che la transazione è stata eseguita
nuovamente non rappresenta più un problema per
la consistenza, e quindi può essere cancellata
e la modalità dirty può essere resettata.
Conclusioni
Con
il suo sistema di caching e il suo file system, Slide
realizza due esempi di sistemi transazionali. In questo
articolo è stato mostrato come questi sistemi
funzionano e quali sono le loro limitazioni e i loro
svantaggi. In generale, svolgono un buon lavoro considerando
che cosa richiedono le applicazioni di Slide. Con le
prossime evoluzioni di Slide, di certo potrebbe aumentare
la gamma delle possibili applicazioni: certamente è
necessario al momento un lavoro sulla cache per migliorarne
il livello di isolamento. Altro lavoro include miglioramenti
sulla scalabilità del motore di ricerca, un miglior
supporto delle versioni, un aumento generale delle prestazioni,
il clustering, transazioni esterne ed ulteriori implementazioni
al livello dei database. Il progetto Slide sta costantemente
cercando nuove persone interessate ad una di queste
aree che siano disposte ad impegnarsi.
Note
sull'autore
Oliver
Zeigermann vive ad Amburgo, Germania e lavora per un'azienda
chiamata C1 Financial Services GmbH. La gamma dei suoi
argomenti tecnici preferiti spaziano dall'analisi di
compilatori e di parsing ai sistemi transazionali, concorrenti,
di database. È il responsabile del rilascio della
release 2.0 del progetto Jakarta Slide. Oliver può
essere raggiunto all'indirizzo oliver@zeigermann.de
|