Untitled Document
   
 
Jakarta Slide's Transactional Storage System
di Oliver Zeigermann - traduzione di Lorenzo Manzoni

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:

  1. Prima che il processo di commit sia iniziato e che lo STATUS_COMMITTING sia stato scritto su disco
  2. Dopo questo, ma prima che lo STATUS_COMMITTED sia stato scritto
  3. Dopo che lo STATUS_COMMITTED è stato scritto
  4. 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