L’articolo
è del ’94. La tesi, in breve, è che non è praticamente possibile rendere
trasparente (invisibile) la differenza fra oggetti locali e remoti, in un
sistema di programmazione ad oggetti. La qual cosa, detta così, può anche
apparire ovvia; ma in quegli anni si faceva un gran parlare di DCOM, Corba,
RMI, EJB e altri sistemi che promettevano al programmatore di “sviluppare
un’applicazione ad oggetti, e preoccuparsi dopo di come distribuire gli
oggetti su diverse macchine.”
Il tono dell’articolo è accademico-scassapalle. Ciononostante hanno ragione. Alcune degli argomenti che citano sono abbastanza banali; altri un po’ meno, soprattutto quello che dicono dei fallimenti parziali.
Quali sono, dunque, i motivi per cui un oggetto remoto non può essere trattato come un’oggetto locale?
-
Latenza. L’invocazione di un metodo locale costa pochissimo, invocare un metodo remoto no. Finchè si tratta di invocarlo una volta è un conto, ma in pratica un sistema distribuito deve essere progettato per minimizzare il numero di network round-trip (chiamate remote). Questo dovrebbe essere abbastanza ovvio.
-
Modello della memoria. Chiamare un metodo significa passare parametri, e magari ricevere dei risultati. Se questi dati sono value objects (semplici strutture dati autocontenute) non c’è nessun problema. Ma se contengono risorse a strutture date esterne, tipo connessioni a database o a file aperti, questi oggetti non si possono serializzare. Così come sono problematiche strutture dati che contengono puntatori ad altre zone di memoria. Anche questo mi pare abbastanza ovvio.
-
Fallimenti parziali. Quando una chiamata remota fallisce, non è dato sapere se il fallimento è totale o parziale. Per esempio: supponiamo di avere una chiamata remota il cui effetto è aggiungere un record ad un database. Se invoco questo metodo, e dopo un po’ di tempo non ho avuto risposta, i casi sono due: o la chiamata è andata persa, e il record non è stato aggiunto; oppure la risposta è andata persa, e il record è stato aggiunto. Il mio problema è che non posso sapere in quale dei due casi mi trovo.
Quest’ultimo caso è il più difficile e interessante. Così su due piedi, l’unica cosa che posso fare è attendere fino a un certo tempo e poi arrendermi. Ma non so se il record è stato aggiunto o no. Tutto quello che posso fare è aspettare un po’ e provare ad aggiungerlo di nuovo, correndo il rischio di aggiungere il record due volte!
La soluzione è semplice, mi dirai. Basta interrogare il server per sapere se il record è stato aggiunto. Oppure aggiungere un identificatore alla mia richiesta; ad esempio, se ho ricevuto una timeout sulla richiesta n. 456, riprovo a mandare la richiesta 456. Il server può così distinguere il caso di una richiesta che ha già visto, nel qual caso la ignora.
Aha! Ma in entrambi i casi, abbiamo modificato l’interfaccia remota per tenere conto del fatto che l’invocazione è, appunto remota.
Nel caso di una invocazione locale non abbiamo i fallimenti parziali: se la mia richiesta di aggiungere un record fallisce, lo so e basta.
Qui sta il succo dell’articolo: per progettare un interfaccia da usare in remoto, occorrono accorgimenti specifici per il caso remoto. Il che può apparire ovvio, detto così! Ma quando ci si prova, è difficile tenere conto dei fallimenti parziali che possiamo avere in un sistema distribuito.
Tutto sommato, è valsa la pena di leggerlo.
PS. Jim Waldo, uno degli autori di “A Note” e in seguito inventore di Jini, tiene un corso ad Harvard la cui pagina è una bella bibliografia di articoli sui sistemi distribuiti
]]>