I modelli di consistenza sono contratti che assicurano un determinato comportamento
in relazione ai dati (data centric), o al client (client centric).
Qui vedremo i principi di gestione delle repliche ed elencheremo alcuni esempi
di implementazione.
Per la gestione delle repliche bisogna considerare
A sua volta il problema del posizionamento può essere suddiviso in due:
La replica dei contenuti e il loro posizionamento può essere considerato
da un punto di vista di organizzazione logica a seconda del tipo di copie del
data store:

Gli item contrassegnati come replica permanente sono l’insieme iniziale delle
repliche, che costituiscono il data store. Ad esempio, un sito web ad alto traffico
può essere replicato su più server acceduti tramite switch in load balancing.
Oppure in mirroring il client sceglie su quale server accedere.
Come detto in precedenti lezioni, la replica può essere comandata dal server
(server-initiated replicas).
Ad esempio esistono analizzatori di traffico web in grado di individuare
a runtime le zone da cui arrivano le richieste. Se il sistema
osserva un carico costante da una determinata zona, può decidere di eseguire
una replica a runtimesu in un server localizzato nella zona target, per aumentare
la velocità di risposta e abbattere l’impegno di rete.
Questo stesso principio (porre il dato in una località più vicina possibile
all’utilizzatore) viene utilizzato anche per l’invio di dati massivi (video),
ed è un principio generale. Si noti che questo tipo di replica può essere
decisa solo dal server: è questa macchina che può analizzare tutte le richieste,
i client non hanno questa visibilità.
Una implementazine efficace di questo principio non è semplice. Vanno considerati diversi
fattori, inclusa la dimensione dei dati da copiare. Se è necessario copiare
grandi quantità di dati, è possibile che l’attività di replica venga completata
in tempi lunghi, quando potrebbe essere superata la necessità che ha dato il
via all’operazione.
Consideriamo ora le repliche comandate dai client (client-initiated replicas).
In questi casi, tipicamente si parla di cache. E si tratta di repliche parziali
del contenuto del server, non di repliche totali.
Se il dato richiesto è in cache, il vantaggio è notevole: non vi è nessun impegno
né di rete, né del server: il client carica il dato presente localmente in
tempi brevissimi. Per contro, è necessario capire se l’informazione in cache
è valida o meno, e tipicamente un dato in cache si tiene per una quantità di tempo
limitata. Ad esempio, nel caso di web browser, questo se ha una pagina in cache,
chiede al web server il solo HEAD della pagina, al fine di controllare la validità
del contenuto in cache. Se confermato, usa la cahe, altrimenti prosegue con una
GET della pagina dal server, visualizzandola e sostituento quella in cache.
Il problema principe delle repliche è mantenere la loro consistenza. Se i dati fossero
completamente statici sarebbe tutto ok: una volta eseguita una replica, non
si sposta più nulla. Purtroppo non è così. I dati cambiano, e le repliche vanno allineate di
conseguenza. Se gli aggiornamenti sono molto frequenti, si può avere un eccesso
di traffico per l’aggiornamento delle repliche tale da saturare il sistema.
Per aggiornare le repliche a fronte di un update, si può:
La terza opzione consiste nell’inviare alla replica un invaliation message
che avverte la replica del fatto che è divenuta (parzialmente) inconsistente. Il
messaggio può specificare quale parte del data store deve essere aggiornato.
In tal modo si impegna meno banda passante. Da'
buoni risultati quando le letture
sono molte meno degli aggiornamenti. Questo è un esempio di lazy consistency:
aggiornerò quando potrò.
Se la lazy consistency può andare bene per i server Google, non è così per
repliche di server con DB bancari. In questo caso non è accettable sapere che un DB
è non valido: li vogliamo tutti allineati come contenuti.
In questi casi, o si trasferiscono i dati modificati (quando il rapporto letture/scritture è
relativamente elevato), o si invia l’operazione di update da effettuare (active replication).
Un altro aspetto da considerare è il verso dell’operazione di aggiornamento :
push, quando l’aggiornamento è spinto verso chi deve essere aggiornato;
pull, quando chi deve essere aggiornato richiede l’aggiornamento.
In caso di approccio push-based gli update sono propagati alle repliche
senza che queste ultime chiedano di essere aggiornate. Sono protocolli
utilizzati dai server e sono applicati quando le repliche devono essere mantenute
identiche, ad es. in ambito bancario. Il rapporto letture/update per ogni replica
è relativamente elevato.
Se l’approccio è pull-based, un server o un client richiede ad un altro server
di inviargli ogni update che è pronto al momento della richiesta. Viene utilizzato
spesso per la gestione delle cache dei client. Risulta efficiente se il rapporto
letture/scritture è relativamente basso.
Queste modalità di lavoro dipendono anche dal modo in cui interagiscono i
programmi nella sorgente rispetto la destinazione:
Zigbee prevede entrambe le modalità di funzionamento: il controllore può
fare pull dei dati dalle periferiche, o si possono configurare periferiche che facciano push
verso router e/o controller.
Un esempio di pull è la gestione della cache da parte dei web browser nel client.
In pratica il web browser controlla la cache locale per verificare se vi è l’oggetto
da richiedere. Se l’oggetto è in cache ed è valido (aggiornato) allora il browser
lo visualizza. Il protocollo HTTP supporta questo tipo di comportamento tramite la
richiesta If-Modified-Since, cui il server risponde inviando la risorsa
cercata solo se è stata modificata dopo il timestamp indicato nella richiesta.
Un protocollo di consistenza descrive una implementazione di uno specifico
modello di consistenza.
Questo è un protocollo centralizzato. Ha la proprietà che tutti i processi
vedono tutte le operazioni di write nello stesso ordine.
Viene illustrato dal seguente diagramma in cui viene utilizzato per mantenere
le repliche consistenti.
Client 1 invia W1: una richiesta di modifica dell’item x. La replica
che riceve la richiesta la inoltra (messaggio W2) al server primario per x.
Questo invia il comando di update (messaggio W3) a tutti e si pone in attesa dei
messaggi di conferma. Quando il server primario ha ricevuto tutte le conferme
(messaggi W4) viene inviato al Client 1 il messaggio W5 di update completato .
In questo contesto vi è sequential consistency perché l’ordine delle richieste di
update è controllato dal (solo) server primario.
Si noti che questo schema non certifica che l’ordine delle operazioni sia quello
di sottomissione da parte dei client, ma quello di ordinamento da parte
del server primario. Server primario che può ricevere due richieste di client
diversi con un ordine invertito rispetto l’originale.
Praticamente tutti i prodotti commerciali di replica sono primary based. Per
avere qualcosa di più evoluto si deve ricorrere alla blockchain.
Questa architettura è push: il primary server spinge gli aggiornamenti verso
le repliche.
Questo è simile al primary-based, ma con la particolarità che il ruolo di
primario viene assunto dinamicamente dal server che ha ricevuto la richiesta.
In questo caso, client2 invia il messaggio di update W1 sull’item x. La replica
che ha ricevuto l’ordine avoca a se il ruolo di primario per l’item x. Il vecchio
primario gli comunica l’avvenuto cambio di ruolo inviando il messaggio W2 . A
questo punto il nuovo primario può eseguire l’update richiesto e rispondere
a client2 che l’operazione è eseguita inviando il messaggio W3. Dopo di che
invia l’ordine di update a tutte le altre repliche (messaggio W4), e si mette in
attesa delle risposte. Quando tutte le risposte W5 saranno arrivate,
il data store sarà nuovamente allineato .
Si nota la differenza dei tempi per l’invio di operazione completata rispetto
il caso di primary-based. In quel caso il client veniva sganciato al termine di
tutte le operazioni di aggiornamento dopo la ricezione di tutti gli ack (messaggio
W5 dello schema primary-based). Qui invece la comunicazione di ok al client
è al terzo passaggio (messaggio W3 dello schema local-write), non appena il nuovo
primario ha assunto il ruolo e ha fatto la propria modifica.
Si osserva che in questi schemi nulla si assicura riguardo il comportamento in fase di lettura.
Si può ipotizzare che durante le fasi di update, client diversi leggano valori diversi dell’item
x, in funzione di quale replica viene utilizzata per la lettura e a che punto è arrivata
la replica nella fase di aggiornamento.