In questo post affrontiamo il problema della comunicazione tra agenti software distribuiti sulla rete, che hanno come obiettivo comune quello di mantenere sincronizzato lo stato di un oggetto gestito contemporaneamente da tutti gli agenti, attraverso lo scambiandosi di messaggi. A tale scopo viene proposto un protocollo di comunicazione (T-Msg) i cui principi fondamentali sono ispirati a quelli del two phase commit (2PC), utilizzato nella gestione delle transazioni tra DBMS distribuiti, e del Transmission Control Protocol (TCP), alla base della comunicazione internet.
Un primo scenario
Per comprendere meglio le difficoltà legate a questo tipo di interazione consideriamo il caso semplice (si fa per dire) di due soli agenti, un sender ed un receiver. Entrambi gestiscono lo stato di un oggetto le cui istanze sui due sistemi sono Os ed Or e che inizialmente sono perfettamente sincronizzate allo stato S1. Il sender decide di aggiorna lo stato dell’oggetto Os ad S2 ed invia, a tal fine, un messaggio M di notifica al receiver, il quale, ricevuto il messaggio, aggiorna lo stato di Or ad S2. I problemi che possono presentarsi durante il processo di aggiornamento dello stato dell’oggetto distribuito sono diversi:
-
il sender potrebbe aggiornare lo stato dell’oggetto prima di riuscire a inviare il messaggio, ad esempio perché cade il nodo che lo ospita;
-
il sender aggiorna lo stato dell’oggetto ed invia il messaggio di notifica ma la connessione cade, ad esempio perché il nodo del receiver cade, ed il messaggio non viene ricevuto;
-
anche nel caso in cui il messaggio fosse pervenuto al receiver il sender non ne avrebbe mai la certezza.
I tre casi precedenti hanno come effetto che il sender non sa quando può effettivamente eseguire il commit sulla propria base dati per confermare l’aggiornamento dello stato dell’oggetto Os.
La situazione si complica ulteriormente se si considera che il sender potrebbe mandare in sequenza più messaggi di notifica il cui ordine di invio riflette evidentemente l’ordine in cui gli stessi dovrebbero essere elaborati dal receiver.
Messaggio ACK
Una prima parziale soluzione al problema consiste nel prevedere, nel protocollo di comunicazione tra i due agenti, un messaggio di Acknowledge (ACK) che il receiver dovrebbe inviare al sender per confermare la ricezione del messaggio. In questo modo il sender potrebbe ritardare il commit sino alla ricezione dell’ACK. Sfortunatamente gli stessi problemi descritti in precedenza si presenteranno anche per il receiver, che evidentemente avrà la difficoltà di comprendere quando eseguire il commit:
-
il receiver aggiorna lo stato dell’oggetto Or ed il nodo che lo ospita cade;
-
il receiver invia l’ACK ma la connessione cade;
-
il recever non ha nessuna conferma della ricezione dell’ACK da parte del sender.
La soluzione
Il protocollo che è necessario adottare per garantire la transazionalità nella sincronizzazione dell’oggetto distribuito è ben più complesso e si basa sullo scambio di più messaggi asincroni tra sender e receiver. Innanzitutto ai messaggi deve essere associato un sequence number, che ne definisce l’ordine di invio in modo da impedire agli agenti di elaborare il messaggio i-esimo sino a quando non abbiano elaborato tutti i messaggi precedenti. Ovviamente deve essere prevista una fase di handshaking iniziale per la sincronizzazione dei sequence number che sarà oggetto di un successivo post.
Analizziamo innanzitutto il funzionamento del protocollo, che nel seguito indicheremo con il termine “Transactional Messagging” (T-Msg), con l’ausilio dell’immagine seguente.
Uno degli agenti (SENDER) decide di modificare lo stato dell’oggetto condiviso. Individua quindi il successivo sequence number disponibile (l’i-esimo nell’esempio) ed invia (SEND) il messaggio di notifica Mi agli altri agenti (RECEIVER1 e RECEIVER2), registrando l’istante di invio (timestamp) e lo stato di ricezione per ciascuno dei destinatari. Nell’esempio S1 e S2 indicano rispettivamente spedito (SENT) agli agenti 1 e 2. Una volta ricevuto il messaggio i due agenti registrano l’evento memorizzando il timestamp e lo stato di ricezione (R: RECEIVED) ed aggiornano il proprio sequence number interno ad i, se maggiore. Successivamente inviano al mittente il messaggio di Acknowledge, ACK(Mi).
Ricevuti tutti gli ACK il sender registra: timestamp e stato; quindi invia un ultimo messaggio di CONFIRM(Mi), ricevuto il quale i due rispondono con il messaggo CONFIRMED(Mi).
I tre sistemi potranno infine elaborare il messaggio solamente quando questo sarà nello stato di CONFIRMED e tutti gli altri messaggi ricevuti Mj con j<i sono stati elaborati.
E’ evidente che il T-Msg è un protocollo a due fasi (simile al 2PC) in cui la prima fase ha come obiettivo non tanto l’invio del messaggio (attività che potrebbe essere spostata sulla seconda fase senza compromettere la correttezza del protocollo), ma soprattutto di condividere l’assegnazione del sequence number. La seconda fase ha inizio quando il seder riceve tutti gli ACK ed ha il solo scopo di abilitare tutti gli agenti ad elaborare il messaggio ricevuto. Nella seconda fase non sarà più possibile abortire la transazione ed il protocollo dovrà garantire che ad un certo punto tutti gli agenti abbiano la conferma.
Per comodità distinguiamo le due fasi rispettivamente con i termini di fase di REQUEST (fase 1) e di CONFIRM (fase 2).
Concludiamo qui la trattazione del protocollo T-Msg rimandando ai successivi post la descrizione delle sue ulteriori caratteristiche ed in particolare di come vengono gestiti tutti i casi eccezionali di fallimento.