Presentazione | 1 - Specifica dei requisiti | 2 - Specifica di progetto | 3 - Documentazione Javadoc | 4 - Analisi delle prestazioni | Bibliografia | Indice


5 - Estensioni e ottimizzazioni



5.1 - Applicative

Il sistema è stato sviluppato trascurando volutamente l'aspetto applicativo. Nella direzione di fornire maggiori funzionalità a livello utente le espansioni possibili sono infinite. L'intento iniziale era quello di giungere almeno ad un'integrazione con Usenet.

Nella documentazione javadoc compaiono alcune classi realizzate per dotare l'applicazione Client di un'interfaccia grafica nell'intento di renderla anche eseguibile come applet. Lo sviluppo di queste features è stato arrestato per favorire lo studio di altre estensioni.


5.2 - Caching nel Front End

Nella nostra implementazione, il Front End realizza solo il supporto per la trasparenza e le politiche di scelta del Replica Manager. Il fatto di avere questo componente intermedio separato dall’applicazione Client, consente di implementare molte estensioni per ottenere maggiore efficienza, estensioni in cui il Front End viene ad assumere un ruolo di proxy server per il servizio applicativo.

Strettamente correlato con l’idea di località c’è il fatto che, finché un Front End continua a rivolgersi allo stesso Replica Manager, si può ridurre il numero di messaggi che transitano in entrambe le direzioni: ad esempio, in molti casi non è necessario che un Front End continui a mandare il proprio timestamp al Replica con cui è costantemente in contatto.

L’ottimizzazione con cui si possono ottenere le maggiori performance è senz’altro il caching dei messaggi. Invece di recuperare un messaggio per volta, ed effettuare ogni volta il protocollo di comunicazione, si può pensare di recuperarne un certo quantitativo (al limite tutti quelli di cui non si è ancora a conoscenza) in modo da diminuire l’overhead di comunicazione.

Inoltre, nell’ipotesi di più Client che si servono dello stesso Front End, il caching a questo livello intermedio risulta molto più vantaggioso in termini di consumo di risorse rispetto ad una memorizzazione a livello di Client.

Ancora, si può pensare di mandare le richieste del Client verso il Replica Manager a lotti, quando questo non influisce sull’efficienza percepita dal Client. Update successivi, ad esempio, possono essere spediti in un solo messaggio, assieme alla query che termina il gruppo in modo trasparente per il Client.

E’ evidente comunque che tali scelte entrano in conflitto con eventuali politiche di fault tolerance, per cui è sempre necessaria una precisa analisi dei requisiti dell’applicazione assieme ad appropriate ipotesi di guasto. Un Client infatti potrebbe considerare come stabile e sicuro un proprio update che si trova ancora in memoria nel Front End e alla caduta di quest’ultimo le conseguenze potrebbero essere disastrose.


5.3 - Uso di un supporto per la comunicazione unreliable

Invece di utilizzare le socket-stream, si può pensare di utilizzare un supporto per la comunicazione basato sul protocollo UDP. Come noto, tale protocollo non realizza l’astrazione connessione e tipicamente non garantisce né la consegna ordinata dei messaggi né, soprattutto, l’affidabilità.

L’implementazione originaria di [LLSG92] sfrutta un protocollo di questo tipo, mentre la scelta da noi operata si è orientata sull’uso di TCP.

Questo perché, nonostante le garanzie offerte dalle socket-stream siano sovrabbondanti rispetto alle effettive necessità del progetto, il considerare i ritardi di propagazione e l’eventuale arrivo di messaggi fuori ordine obbliga a tenere conto di determinati vincoli temporali che, oltre ad appesantire notevolmente l’algoritmo, necessitano di una sincronizzazione lasca degli orologi tra le macchine che ospitano i Replica Manager.

L’adattamento del sistema all’uso di un supporto per la comunicazione di tipo unreliable, può essere fatto operando per prima cosa sui metodi base delle classi che realizzano le strutture dati di cui si servono i Replica Manager, poi adattando i protocolli di comunicazione: basterebbe seguire per intero quanto descritto in [LLSG92].

E’ però necessario sottolineare che, nonostante l’adozione di un supporto di tale tipo comporti indubbi benefici a livello di efficienza, la scelta dei parametri che dipendono dai tempi di latenza per le comunicazioni sulla rete e dall’inevitabile clock-skew risulta molto delicata.


5.4 - Dinamicità dell'insieme di Replica Manager

Il sistema così com'è strutturato e descritto in [LLSG92] è statico, cioè è costituito da un numero di Replica Manager noto a priori e fisso. Un Replica Manager può cadere ed essere ripristinato, ma il numero di copie attive non può aumentare durante il funzionamento.

Si vuole rendere il sistema dinamico, cioè si vuole poter aumentare il numero di Replica Manager senza dover fermare e modificare ogni copia.

Attualmente il numero di server che fanno parte del sistema è un parametro che deve essere fornito ad ogni processo che implementa un Replica Manager affinché si possano inizializzare ed usare correttamente le strutture dati usate per il coordinamento, in particolare i multipart timestamp. Un altro parametro, indispensabile ad ogni Replica Manager, è un intero che lo identifica univocamente all'interno del sistema. A fronte dell'aumento del numero di Replica Manager è necessario sia adattare le strutture dati usate per le informazioni di coordinamento, sia assegnare ai nuovi Replica Manager identificativi univoci universalmente validi. Ciò sembra implicare la necessità di una ulteriore fase di coordinamento tra le copie attive ogni volta che si vuole inserire un nuovo server nel sistema.

Una soluzione potrebbe consistere nell'utilizzare un algoritmo di elezione che sfrutti magari l'ordinamento tra i Replica Manager dovuto alla presenza degli identificativi. Una soluzione più economica consiste nel rinunciare ad assegnare esplicitamente ad ogni copia un identificativo univoco e ad utilizzare gli indirizzi IP degli host su cui girano i Replica Manager (nell'ipotesi che ad ogni IP corrisponda un solo Replica Manager). Un Replica Manager che volesse entrare a far parte del sistema di copie, avrebbe già automaticamente assegnato un identificativo univoco. E' necessario poi trasformare i multipart timestamp da semplici vettori di contatori, di dimensione pari al numero di Replica Manager, in strutture che associno ad ogni IP un contatore e siano in grado di variare di dimensione per l'inserimento di nuovi IP. Dal punto di vista implementativo, i multipart timestamp erano array di interi di dimensione prefissata. La struttura ideale sembra ora essere una hash table di contatori interi acceduta mediante IP. Uno svantaggio evidente ma non grave è che le comunicazioni che prima vedevano scambi di semplici array ora si appesantiscono leggermente. La classe usata per implementare i multipart timestamp del sistema reso dinamico è DMultipartTimestamp.

Il risultato dell'operazione di merge tra due DMultipartTimestamp è ora un DMultipartTimestamp che ha gli indirizzi di entrambi e i contatori maggiori tra i due per ogni indirizzo. La relazione di ordine parziale tra multipart timestamp va modificata stabilendo che un DMultipartTimestamp t1 è minore o uguale di un DMultipartTimestamp t2 se ogni elemento di t1 è non maggiore del corrispondente elemento di t2 e t1 non ha nessun indirizzo che t2 non conosca già. Questa estensione si basa sull'assunzione che nuovi Replica Manager possano essere aggiunti al sistema mentre nessuno lo possa lasciare.


5.5 - Topologia dinamica delle interconnessioni

La configurazione statica della topologia di interconnessione tra i Replica Manager implica l’editing dei file config.properties presenti su ogni macchina per garantire una conoscenza locale della allocazione fisica dei server remoti. In pratica, quando il numero delle copie replicate comincia ad aumentare, la modifica di tali file risulta scomoda, nonché fonte di errori difficili da individuare. Si può pensare allora ad una rete di Replica Manager che, iniziando con una conoscenza minimale del sistema, sia in grado di scambiare oltre ai messaggi di gossip anche informazioni riguardanti la disposizione fisica delle macchine nel sistema. Per fare questo è sufficiente effettuare una piccola modifica al protocollo di gossip; ricordiamo che le informazioni relative agli indirizzi ip degli host remoti sono mantenute in una tabella in memoria. Convenzionalmente, un riferimento a null in una cella di tale tabella indica che non si conosce l’indirizzo del Replica Manager corrispondente. Il GossipScheduler ovviamente effettua delle richieste di gossip solo verso i Replica Manager che conosce, ma può accadere di dovere processare una richiesta di gossip da parte di un host il cui indirizzo non compare nella tabella. Supponiamo allora di dare la conoscenza minima possibile dell’allocazione fisica delle macchine ai Replica Manager presenti nel sistema: otterremo uno spanning-tree che connette i vari nodi. Se si fa in modo che un replier memorizzi l’indirizzo di ogni Replica Manager che non conosce e con cui viene in contatto, è possibile dimostrare che la conoscenza della topologia si distribuirà finché non si giunge al punto di completa interconnessione. Per fare questo non è nemmeno necessario modificare il protocollo di gossip, poiché l’indirizzo ip remoto può essere ricavato dalla socket a cui si è connessi: è sufficiente modificare il codice della classe GossipReplier.


5.6 - Eliminazione dell'attesa attiva dei CallMsgManager

In applicazioni non time-critical, risulta conveniente tralasciare la politica di gossip by-need, sospendendo il thread del Replica Manager che non può portare a termine la richiesta del Front End fino a che, in seguito all’arrivo di un gossip periodico, esso non potrà disporre di dati più aggiornati. Questo consente di diminuire il numero di connessioni e conseguentemente di ottenere maggiore efficienza.

Nella nostra implementazione, il thread CallMsgManager impossibilitato a proseguire si sospende per un determinato periodo di tempo prima di tornare a testare la condizione che lo ha sospeso. Dal punto di vista dell’efficienza, una possibile ottimizzazione è quella di fare risvegliare questo thread dal GossipReplier che riceve l’aggiornamento atteso, ed eliminare così un certo numero di cicli di busy-waiting. Per far questo, bisogna implementare una struttura dati che mantenga una coda di thread sospesi in attesa di un particolare evento, così come avviene nei sistemi operativi multiprocesso. In Java è però sufficiente fare in modo che i thread CallMsgManager si sospendano sull’accesso ad uno specifico oggetto, in modo che sia possibile il loro risveglio selettivo da parte di un eventuale dispatcher.


5.7 - Ordinamenti forced e immediate

Alcune applicazioni che sfruttano operazioni ordinate con il criterio della causalità potrebbero occasionalmente richiedere un ordinamento più vincolante. La disponibilità di un protocollo che permette di specificare la priorità di un’operazione consente di estendere l’approccio gossip ad un maggior numero di casi reali. E’ chiaro comunque che tale approccio consente di ottenere vantaggi significativi solo in quei casi in cui le operazioni di tipo causale rappresentano la maggior parte del lavoro svolto dal sistema, mentre altri tipi più vincolanti di ordinamento vengono usati più raramente. Supporremo quindi che ad ogni operazione eseguita dal sistema sia associato un codice di ordinamento: distingueremo tra operazioni causali (a default), forced e immediate.

Le operazioni forced vengono eseguite nello stesso ordine da tutti i Replica Manager, mentre il loro ordinamento rispetto alle operazioni causali non è rilevante. Supponiamo ad esempio che, in una applicazione con controllo degli accessi, due Replica Manager cerchino di aggiungere al sistema un utente con lo stesso username: solo uno dei due dovrà portare a termine l’operazione con successo. Utilizzando un ordinamento di tipo forced, viene garantito che le due richieste si presenteranno tra loro nello stesso ordine a tutti i Replica Manager, in modo che la prima vada a buon fine e la seconda fallisca.

Le operazioni classificate immediate vengono invece eseguite da tutti i Replica Manager nello stesso ordine rispetto a tutti gli altri tipi di operazioni: l’effetto è in pratica quello di essere eseguite immediatamente e in modo consistente da tutti i server. Ci sono vari comandi che in un sistema devono essere eseguiti con questa semantica: ad esempio non si deve permettere che un utente a cui sono già stati revocati tutti i privilegi sia in grado di accedere al servizio a causa del ritardo di propagazione della notifica. Come si può notare, l’implementazione di questi meccanismi di ordinamento riguarda solo le operazioni classificate come update, mentre quelle di tipo query continueranno ad essere ordinate in base al proprio multipart timestamp.

Implementare entrambe queste politiche risulta fondamentale in previsione di un estensione del progetto sul versante della sicurezza, in particolare per ciò che riguarda il controllo degli accessi.


Presentazione | 1 - Specifica dei requisiti | 2 - Specifica di progetto | 3 - Documentazione Javadoc | 4 - Analisi delle prestazioni | Bibliografia | Indice