Programmazione Concorrente e Distribuita — Prof. Alessandro Ricci

Algoritmi Distribuiti: conclusioni + Service-Oriented Computing

2026-05-18128 min registrazione originale

In questa lezione

1. Quadro generale e Assignment 4

Questa lezione conclude il modulo sugli algoritmi distribuiti (module 4.2) e introduce l'architettura Service-Oriented (module 4.3). Il professore dedica la prima parte all'Assignment 4, che mette in pratica due paradigmi fondamentali del distributed computing: il message passing con il modello degli attori, e il distributed object computing con Java RMI.

Idea chiave

L'Assignment 4 chiede di scegliere consapevolmente tra due filosofie: attori (messaggi asincroni, isolamento, nessuna memoria condivisa) versus RMI (chiamate a metodo sincrone, trasparenza oggettuale, middleware per la distribuzione). Non esiste una scelta giusta in assoluto: dipende dal problema da risolvere.

Assignment 4 — i due esercizi obbligatori

Il primo esercizio riguarda gli attori come modello di riferimento per il message passing, utilizzando un middleware a scelta (Akka, Quasar o altri). Il secondo esercizio applica la visione del distributed object computing con Java RMI. Il professore propone come esempio un sistema distribuito per giocare a Tic-Tac-Toe (tris) dove due giocatori su nodi diversi interagiscono attraverso oggetti remoti. La consegna e volutamente con pochi requisiti: lo studente deve fare le scelte progettuali in autonomia.

Per l'esame

Il professore sottolinea che non bisogna affidarsi ciecamente all'AI per questi esercizi: "se vi fate aiutare dalle AI, fatemi aiutare bene, perche non e detto che vi aiuti bene". I principi del corso (incapsulamento del flusso di controllo, componenti attivi/passivi, modularita) sono concetti che un assistente AI non padroneggia automaticamente. E molto meglio pensare e programmare toccando con mano.

Java RMI come esempio di middleware per distributed object computing

Java RMI (Remote Method Invocation) e un middleware che implementa il paradigma del distributed object computing. Consente di invocare metodi su oggetti che risiedono in JVM diverse, potenzialmente su macchine differenti. L'architettura si basa su quattro pilastri:

Il professore spiega che la RemoteException puo manifestarsi in scenari diversi: il nodo remoto non e raggiungibile al momento della chiamata, oppure la connessione cade durante l'esecuzione del metodo, prima che il risultato torni indietro. In tutti i casi, e lo stub (proxy) a gestire la situazione e sollevare l'eccezione appropriata.

Nota del redattore

Il professore accenna anche a RabbitMQ come esempio di message-oriented middleware (MOM), visitandolo brevemente nell'ottica dell'Assignment 4. La differenza fondamentale: RMI e sincrono e orientato agli oggetti, RabbitMQ e asincrono e orientato ai messaggi. Uno e basato su distributed object computing, l'altro su message-oriented middleware.

2. Sfide degli algoritmi distribuiti

I sistemi distribuiti presentano tre assenze fondamentali che li rendono radicalmente diversi dai sistemi concorrenti tradizionali (basati su memoria condivisa):

La maggior parte degli algoritmi distribuiti assume sistemi asincroni in assenza di guasti (ritardo di consegna finito ma non noto a priori), mentre per gestire i guasti si passa a modelli sincroni con timeout.

Non esiste un limite superiore noto al tempo di consegna dei messaggi. Il ritardo puo essere arbitrario ma finito. La maggior parte degli algoritmi distribuiti assume reti asincrone in assenza di guasti. Il nondeterminismo e massimo, la concorrenza e massima. Ideali per ambienti non critici dove la probabilita di guasto e bassa.

Esiste un limite superiore noto al tempo di consegna dei messaggi e alla durata delle azioni dei processi. Si possono usare timeout per rilevare guasti. La progettazione degli algoritmi e piu semplice. Necessari quando si devono gestire guasti, ad esempio negli algoritmi di consensus o nei protocolli bizantini.

Categorie principali di algoritmi distribuiti

Il professore elenca le cinque macro-categorie che gli algoritmi distribuiti devono affrontare, tutte basate sugli orologi logici e sulla relazione happened before gia introdotta nel modulo precedente:

  1. Ordinamento degli eventi (parziale): orologi logici di Lamport, vector clocks. Sono il fondamento usato da tutti gli altri algoritmi
  2. Osservazione dello stato: global snapshot per catturare lo stato globale del sistema da un singolo processo
  3. Coordinamento e accordo: mutua esclusione distribuita, elezione del leader, multicast communication con ordinamento, consensus
  4. Transazioni distribuite e controllo della concorrenza
  5. Replicazione e servizi fault-tolerant
Idea chiave

Tutti questi algoritmi si basano sugli orologi logici e sulla relazione happened before (→) per definire un ordinamento tra eventi correlati. Il concetto di tempo logico di Lamport (1978) e il fondamento su cui poggia l'intera architettura degli algoritmi distribuiti.

Modelli di computazione distribuita

Il professore richiama i tre modelli principali per descrivere il comportamento di un programma distribuito:

3. Mutua esclusione — algoritmo centralizzato

Il problema della mutua esclusione in ambito distribuito e analogo al caso concorrente, ma i processi sono distribuiti su nodi diversi e comunicano solo via messaggi. Le proprieta classiche di una sezione critica (CS) sono tre:

Algoritmo centralizzato con coordinatore

Esiste un coordinatore (processo P0) che gestisce un token. I processi client (Pi) inviano una richiesta al coordinatore, che concede il token quando possibile. Il punto chiave e la fairness: anche se una richiesta t arriva al coordinatore prima di una richiesta s che la precede causalmente, il coordinatore deve capire che s e anteriore e darle precedenza.

Per garantire la fairness, ogni processo include il proprio vector clock v appeso ai messaggi in uscita (piggybacking). Il coordinatore usa queste informazioni per ritardare una richiesta finche tutte le richieste che la precedono causalmente non sono arrivate. Il coordinatore mantiene due strutture dati:

Alla ricezione di una richiesta o del token, il coordinatore esegue checkReq: una richiesta w e eligible se per ogni j diverso da w.p si ha w.v[j] == reqDone[j], e per j == w.p si ha w.v[j] == reqDone[j] + 1. In altre parole, non ci sono richieste accadute prima di w che non sono ancora state soddisfatte.

sequenceDiagram
    participant P1 as Pi (client)
    participant C as P0 (coordinator)
    participant P2 as Pj (client)

    Note over P1: v[i]++, send(req, v)
    P1->>C: (req, v = [1,0,0,...])
    Note over C: append reqList
if haveToken: checkReq() Note over C: w = first(eligible)
send token, incr reqDone C->>P1: token Note over P1: inCS = true Note over P1: [sezione critica] P1->>C: token (release) Note over P1: inCS = false Note over C: haveToken = true
checkReq() again C->>P2: token (prossimo eligible)

Il processo client Pi, quando riceve un messaggio da un altro processo, aggiorna il proprio vettore v con il component-wise max del suo vecchio valore e del vettore ricevuto. Questo garantisce che la conoscenza causale si propaghi correttamente.

4. Mutua esclusione — Ricart-Agrawala (decentralizzato)

L'algoritmo di Ricart-Agrawala (1981) e una soluzione decentralizzata che non richiede un coordinatore centralizzato. Ogni processo comunica con tutti gli altri per ottenere il consenso ad entrare nella sezione critica. E considerato un algoritmo ottimale per il numero di messaggi.

Idea

Il funzionamento si basa su quattro regole semplici:

Numero di messaggi: 2(N-1) per ogni accesso alla sezione critica. L'algoritmo funziona anche con canali non FIFO, perche l'ordinamento causale e garantito dai timestamp logici.

5. Elezione del leader — Chang-Roberts

L'elezione del leader e il problema di scegliere un processo tra un insieme di N processi. Viene usata per selezionare coordinatori negli algoritmi centralizzati. Una strategia ampiamente utilizzata nei sistemi distribuiti reali.

Algoritmo di Chang-Roberts (1979)

L'algoritmo sovrappone una topologia ad anello logico alla rete sottostante. Ogni processo ha un PID unico. L'idea e semplice: il processo con il PID massimo viene eletto leader.

Comportamento

graph LR
    A[P2
id=2] -->|election,2| B[P7
id=7] B -->|election,7| C[P3
id=3] C -->|election,7| D[P1
id=1] D -->|election,7| E[P5
id=5] E -->|election,7| F[P4
id=4] F -->|election,7| A A -->|leader,7| B B -->|leader,7| C C -->|leader,7| D D -->|leader,7| E E -->|leader,7| F F -->|leader,7| A

Complessita nel caso peggiore: 2N-1 messaggi di election + N messaggi di leader.

Idea chiave

L'algoritmo usa l'anello logico solo per il controllo dell'elezione, non per il flusso dei dati applicativi. E un esempio classico di superimposing: sovrapporre un protocollo di coordinamento a una topologia fisica arbitraria. Questo pattern si ritrova in molti algoritmi distribuiti.

6. Ordinamento causale dei messaggi

In un sistema completamente asincrono non ci sono restrizioni sull'ordinamento dei messaggi. Questo massimizza il nondeterminismo e la concorrenza, ma a volte e utile ridurre il nondeterminismo imponendo vincoli sull'ordinamento per semplificare la programmazione.

Dal FIFO all'ordinamento causale

L'ordinamento FIFO garantisce che se due messaggi sono inviati dallo stesso processo, arrivano nello stesso ordine. L'ordinamento causale e piu forte: dati due messaggi m1, m2 tali che send(m1) → send(m2), allora rec(m1) → rec(m2). Nessun sorpasso (overtaking) e permesso, anche se i messaggi vengono da processi diversi.

Algoritmo per l'ordinamento causale

L'algoritmo estende i vector clocks con una matrice m di interi (N x N), dove s.m[j,k] rappresenta il numero di messaggi inviati da Pj a Pk noti a Pi nello stato s. Ogni volta che un messaggio viene inviato da Pi a Pj, la matrice viene appesa al messaggio (piggybacked). Alla ricezione, il sistema di comunicazione verifica se il messaggio e eligible prima di consegnarlo al processo.

Un messaggio u e eleggibile per essere ricevuto da Pi quando, per ogni processo k, il numero di messaggi inviati da Pk a Pi riportato in u.m e minore o uguale al numero registrato nella matrice locale di Pi. Se non e eleggibile, il messaggio viene messo in una coda di buffer fino a quando non lo diventa (perche altri messaggi sono arrivati e hanno aggiornato la matrice).

sequenceDiagram
    participant A as P1
    participant B as P2
    participant C as P3

    Note over A: m[1,2]++, send to P2
    A->>B: msg1 (con matrice m)
    Note over B: check eligibility:
m'[1,2] == m[1,2]+1?
se si: update, receive Note over A: m[1,3]++, send to P3 A->>C: msg2 (con matrice m) Note over B: P2 invia a P3 B->>C: msg3 (con matrice m') Note over C: msg3 arriva prima di msg2?
Se send(msg2) -> send(msg3)
allora rec(msg2) -> rec(msg3)
Se msg3 arriva prima, buffer C->>C: buffer msg3
aspetta msg2 A->>C: msg2 arriva (dopo) Note over C: msg2 eligible? si
aggiorna matrice
controlla msg3 bufferizzato
ora msg3 eligible C->>C: deliver msg3

7. Global snapshot — Chandy-Lamport

Catturare lo stato globale di un sistema distribuito da un singolo processo e una sfida fondamentale. Lo stato globale e definito come un insieme di stati locali tutti concorrenti tra loro secondo il modello happened before. Per molte applicazioni, e sufficiente catturare uno stato globale che e esistito nel passato, non necessariamente lo stato corrente.

Attenzione

Uno stato globale non e semplicemente il prodotto degli stati locali. Un taglio inconsistente (inconsistent cut) si verifica quando viene registrata la ricezione di un messaggio ma non la sua spedizione. Formalmente, un global state S e consistente se per ogni evento e = receive(msg), l'evento e' = send(msg) (dove e' → e) appartiene a S.

L'algoritmo di Chandy-Lamport (1985)

L'algoritmo calcola un consistent cut (taglio consistente) dello spazio-tempo distribuito, assumendo canali unidirezionali FIFO. Il meccanismo e elegante e non richiede di bloccare i processi durante la cattura:

Grazie ai canali FIFO, nessun processo bianco riceve mai un messaggio inviato da un processo rosso. Questo garantisce che gli stati locali siano mutuamente concorrenti. Lo stato dei canali e dato dai messaggi inviati da un processo bianco e ricevuti da uno rosso — messaggi che erano in transito quando lo snapshot e stato preso.

8. Consensus: definizione e proprieta

Il consensus e il problema fondamentale del distributed computing: trovare un accordo tra processi distribuiti sul valore di una proprieta o su un'azione da intraprendere. E onnipresente nei sistemi reali: transazioni nei database (commit/abort), leader election, state machine replication, atomic broadcast, sincronizzazione di clock, blockchain.

Definizione formale

Per raggiungere il consenso, ogni processo Pi inizia nello stato undecided e propone un singolo valore vi (da un insieme D). I processi comunicano scambiandosi valori. Ogni processo infine imposta una variabile di decisione di con il valore deciso, entrando nello stato decided dal quale non puo piu cambiare di.

Tre requisiti fondamentali

Risultato fondamentale: FLP

FLP (Fischer, Lynch, Patterson, 1985): in una rete asincrona, anche con un singolo processo che puo morire (crash non annunciato), il problema del consensus e impossibile da risolvere. Questo teorema di impossibilita ha profonde implicazioni pratiche: tutti i protocolli di consensus reali devono rilassare qualche assunzione, tipicamente introducendo sincronia o randomizzazione.

Dal teorema alla pratica: consensus sotto sincronia

Per aggirare FLP, si assume un sistema sincrono (bound superiore sul ritardo dei messaggi e sulla durata delle azioni) e si definiscono modelli di guasto specifici. I modelli principali sono:

ModelloDescrizione
CrashUn processore si arresta (halt). Il guasto piu semplice da modellare
Crash+linkUn processore crasha oppure un link di rete cade e resta giu permanentemente
OmissionUn processo invia solo un sottoinsieme dei messaggi che dovrebbe inviare, o ne riceve solo un sottoinsieme
ByzantineUn processore ha comportamento arbitrario: puo inviare messaggi contraddittori, valori falsi, o agire in modo malevolo. Il caso piu generale e difficile

9. Consensus di base e bizantino

Algoritmo di base (crash-stop, nessun comportamento bizantino)

Ogni processo mantiene V, l'insieme dei valori che conosce essere stati proposti dai processori del sistema. Inizialmente, un processo conosce solo il proprio valore. L'algoritmo procede per f+1 round, dove f e il numero massimo di processori che possono fallire:

Complessita: O((f+1)N^2) messaggi. Funziona solo in assenza di comportamenti bizantini.

graph TD
    subgraph "Round 1"
        P1a[P1: V={v1}] -->|send v1| P2a[P2]
        P1a -->|send v1| P3a[P3]
        P2a[P2: V={v2}] -->|send v2| P1a
        P2a -->|send v2| P3a
        P3a[P3: V={v3}] -->|send v3| P1a
        P3a -->|send v3| P2a
    end
    subgraph "Round 2 (f=1, rounds=2)"
        P1b[P1: V={v1,v2,v3}] -->|send nuovi| P2b[P2]
        P1b -->|send nuovi| P3b[P3]
        P2b[P2: V={v1,v2,v3}] -->|send nuovi| P1b
        P2b -->|send nuovi| P3b
        P3b[P3: V={v1,v2,v3}] -->|send nuovi| P1b
        P3b -->|send nuovi| P2b
    end
    subgraph "Decisione"
        P1c[P1: decide(V)]
        P2c[P2: decide(V)]
        P3c[P3: decide(V)]
    end
    

Consensus bizantino (Byzantine General Agreement)

Il caso in cui i processi guasti possono avere comportamento arbitrario: inviare valori errati, messaggi contraddittori a destinatari diversi, o non inviare nulla. Il problema dei generali bizantini (Lamport, Shostak, Pease, 1982) e un classico: i generali leali (processi corretti) devono coordinarsi (decidere all'unisono se attaccare o ritirarsi) nonostante la presenza di traditori.

Per l'esame

Teorema BGA: non esiste un protocollo f-resilient per il Byzantine General Agreement se N ≤ 3f (N = numero totale di processi, f = guasti). Serve almeno 3f+1 processi per tollerare f guasti bizantini. Ad esempio, con 1 traditore servono almeno 4 generali; con 2 traditori almeno 7.

Algoritmo per il consenso bizantino

L'algoritmo procede in f+1 round con un coordinatore rotante (chiamato king). In ogni round:

Almeno un round avra un king non guasto, garantendo che tutti i processori corretti convergano sullo stesso valore.

10. Paxos, Raft e Replicated State Machines

Paxos (Lamport, 1998)

Paxos e una famiglia di protocolli per risolvere il consensus in una rete di processori non affidabili, considerando solo comportamento crash-stop (non bizantino). Il nome deriva dall'articolo "The Part-Time Parliament" (Lamport, 1998), che presenta il consenso come un parlamento che legifera anche quando alcuni parlamentari sono assenti.

Paxos bilancia diversi aspetti: numero di processori, ritardi di messaggio prima di apprendere il valore concordato, livello di attivita dei partecipanti, numero di messaggi scambiati, e tipi di guasto tollerati. E usato in tutti i contesti dove serve durabilita, come la replicazione di file o database.

Raft (Ongaro, Ousterhout, 2013)

Raft (Reliable, Replicated, Redundant, And Fault-Tolerant) e un algoritmo di consensus progettato per essere facile da capire. Equivalente a Paxos in fault-tolerance e performance, ma significativamente piu semplice da implementare. Offre un modo generico per distribuire una state machine su un cluster di sistemi, garantendo che ogni nodo concordi sulla stessa serie di transizioni di stato.

Raft e implementato open-source in Go, C++, Java e Scala (riferimento: raft.github.io). E utilizzato in progetti come etcd e Consul.

Proposto da Leslie Lamport nel 1998. Famiglia di protocolli, non un singolo algoritmo monolitico. Considerato storicamente il riferimento per il consensus in produzione, ma notoriamente difficile da comprendere e implementare correttamente. Il nome e la metafora del parlamento riflettono la complessita della presentazione originale.

Proposto da Diego Ongaro e John Ousterhout nel 2013. Progettato esplicitamente per l'insegnamento e la comprensione. Usa un leader forte, elezione del leader con timeout randomizzati, e una struttura a log piu chiara. E lo standard de facto per nuovi progetti che necessitano di consensus.

Replicated State Machines

Il consensus emerge tipicamente nel contesto delle replicated state machines (RSM), un approccio generale per costruire sistemi fault-tolerant. Ogni server ha:

L'algoritmo di consensus coordina il log replicato: garantisce che se una state machine applica "set x to 3" come n-esimo comando, nessun'altra state machine nello stesso cluster applichera mai un comando diverso come n-esimo. Tutte eseguono la stessa identica sequenza di comandi, producendo gli stessi risultati e arrivando agli stessi stati.

graph LR
    Client -->|"set x=3, get y, ..."| LB[Load Balancer]
    LB --> SM1[Server 1
State Machine
Log: [set x=3,...]] LB --> SM2[Server 2
State Machine
Log: [set x=3,...]] LB --> SM3[Server 3
State Machine
Log: [set x=3,...]] Consensus[Consensus Module
Raft / Paxos] -.-> SM1 Consensus -.-> SM2 Consensus -.-> SM3 SM1 -.->|replica log| Consensus SM2 -.->|replica log| Consensus SM3 -.->|replica log| Consensus

Il sistema appare ai client come un'unica state machine affidabile, anche se una minoranza dei server nel cluster fallisce. Questo pattern e alla base di sistemi come Google File System (GFS), HDFS, RAMCloud, e innumerevoli database distribuiti.

11. Service-Oriented Computing

Con il modulo 4.3, il professore introduce una prospettiva architetturale e ingegneristica del software distribuito. Mentre gli algoritmi distribuiti risolvono problemi specifici (consensus, mutua esclusione, ordinamento), il Service-Oriented Computing e un paradigma architetturale che abbraccia l'intero ciclo di vita del software distribuito.

Servizi e microservizi

Un servizio e un componente software autonomo, indipendente, con un confine ben definito e un contratto esplicito (tipicamente un'interfaccia). I microservizi estendono questo concetto portandolo all'estremo: servizi piccoli, focalizzati su un singolo dominio di business, deployabili indipendentemente, che comunicano via rete (tipicamente HTTP/REST o messaggi asincroni).

Idea chiave

Il passaggio dal distributed object computing (Java RMI, CORBA) al service-oriented computing non e solo tecnico: e un cambio di paradigma. Invece di nascondere la distribuzione dietro oggetti remoti (trasparenza), i servizi abbracciano esplicitamente la loro natura distribuita, con contratti, interfacce e API ben definite.

Domain-Driven Design (DDD)

Il Domain-Driven Design e strettamente correlato ai microservizi. Il concetto chiave e il bounded context: ogni microservizio dovrebbe mappare esattamente un bounded context del dominio applicativo. Questo garantisce che:

Architetture event-driven e stream-based

Oltre ai microservizi tradizionali (sincroni, REST), il professore menziona due architetture emergenti:

12. Teorema CAP e latenza

Il teorema CAP (Brewer, 2000, formalizzato da Gilbert e Lynch, 2002) e uno dei risultati fondamentali della progettazione di sistemi distribuiti: qualsiasi sistema che condivide dati in rete puo avere al massimo due delle tre proprieta seguenti:

Nota del redattore

Il professore approfondisce un aspetto spesso frainteso: la latenza. Nell'interpretazione classica, il teorema CAP ignora la latenza, ma in pratica latenza e partizioni sono profondamente correlate. Durante un timeout, il programma deve fare una scelta fondamentale: cancellare l'operazione (riducendo la disponibilita) o procedere (rischiando l'inconsistenza).

Latenza come partizione

Il professore spiega che, pragmaticamente, una partizione e un limite di tempo sulla comunicazione. Se non si riesce a raggiungere la consistenza entro quel limite, ci si trova di fronte al partition decision: il bivio tra consistenza e disponibilita. Due nodi che procedono senza comunicare — questo e il dilemma fondamentale.

Questi concetti catturano la questione progettuale centrale riguardo alla latenza: due parti del sistema stanno andando avanti senza comunicazione? La risposta determina se si sta violando la consistenza o la disponibilita.

Dal modello ai meccanismi

Il professore ricorda che dal modello (happened before, potenzial causality) si passa ai meccanismi: orologi logici e vector clocks per implementare il modello happened before. Questi meccanismi sono gli strumenti concreti che i programmatori distribuiti utilizzano per ordinare eventi, rilevare consistenza, e costruire algoritmi corretti.

13. Verso gli assignment pratici

Il professore chiude la lezione tornando sull'Assignment 4 e fornendo indicazioni pratiche per l'implementazione.

Il pattern Proxy nei middleware distribuiti

Il pattern proxy e centrale in entrambi i paradigmi: negli attori (ogni attore ha un indirizzo e comunica solo tramite messaggi asincroni, con un proxy che gestisce l'invio) e in RMI (lo stub e un proxy che serializza le chiamate a metodo in messaggi di rete). Il professore sottolinea che il proxy e responsabile di:

Dalle chiamate di procedura remota al distributed object computing

Il professore ricorda la progressione storica: si parte dalle Remote Procedure Call (RPC), dove chiamiamo funzioni su nodi remoti come se fossero locali. Con Java RMI, si passa alle Remote Method Invocation, che aggiungono la dimensione object-oriented: non chiamiamo piu funzioni, ma invochiamo metodi su oggetti che risiedono in JVM remote. La visione e quella del distributed object computing classico: oggetti che interagiscono chiamandosi metodi a vicenda attraverso la rete.

Per l'esame

Il professore raccomanda di pensare in modo critico alla progettazione: "nel pensare a come progettare lo sviluppo ci sono dei principi che abbiamo visto nel corso importanti e che vengono messi da una descrizione come questa. Se noi diciamo componenti attivi, passivi, incapsulamento del flusso di controllo, sono tutte questioni che non e detto che l'AI subito attacchi, anzi." Il consiglio e di programmare toccando con mano e fare scelte consapevoli.

Conclusione del modulo algoritmico

Con questa lezione si conclude la parte algoritmica del corso. Il percorso e iniziato con la programmazione concorrente (thread, lock, monitor, attori), ha attraversato la programmazione asincrona e reattiva (CompletableFuture, RxJava), ha esplorato i modelli di message passing (attori, CSP), e culmina con gli algoritmi distribuiti (mutua esclusione, elezione, snapshot, consensus). I prossimi moduli si concentreranno sugli aspetti architetturali e operativi del software distribuito, completando il percorso formativo.

Verifica le tue conoscenze

Quali sono le tre assenze fondamentali che caratterizzano i sistemi distribuiti?

Assenza di clock condiviso (non si possono sincronizzare orologi fisici), assenza di memoria condivisa (nessun processo conosce lo stato globale), assenza di rilevamento accurato dei guasti (in un sistema asincrono non si distingue tra processo lento e processo caduto).

Nell'algoritmo di mutua esclusione centralizzato, quando una richiesta w e considerata "eligible"?

w e eleggibile se w.v e "al massimo" uguale a reqDone: per ogni j diverso da w.p si ha w.v[j] == reqDone[j], e per j == w.p si ha w.v[j] == reqDone[j] + 1. Questo significa che non esistono richieste accadute prima di w che non sono ancora state soddisfatte.

Quanti messaggi servono nell'algoritmo di Ricart-Agrawala per un accesso alla sezione critica?

2(N-1) messaggi, dove N e il numero di processi: (N-1) richieste inviate a tutti gli altri processi + (N-1) risposte OK ricevute. E considerato ottimale.

Cosa dice il risultato FLP sul consensus in reti asincrone?

In una rete asincrona, anche con un singolo processo che puo fallire (crash non annunciato), il problema del consensus e impossibile da risolvere. Questo e il celebre risultato di Fischer, Lynch e Patterson (1985), che ha profonde implicazioni pratiche per la progettazione di sistemi distribuiti.

Qual e la condizione per avere un protocollo BGA (Byzantine General Agreement) risolvibile?

N > 3f, dove N e il numero totale di processi e f e il numero di processi guasti con comportamento arbitrario. Se N ≤ 3f, non esiste alcun protocollo f-resilient. Ad esempio, per tollerare 1 guasto bizantino servono almeno 4 processi.

Cosa garantisce l'ordinamento causale dei messaggi rispetto al semplice FIFO?

L'ordinamento causale e piu forte del FIFO: dati due messaggi m1, m2 tali che send(m1) → send(m2), allora rec(m1) → rec(m2). Il FIFO garantisce solo l'ordine all'interno dello stesso processo mittente, mentre l'ordinamento causale vieta i sorpassi (overtaking) anche tra processi diversi.

Cosa dice il teorema CAP di Brewer?

Un sistema distribuito con dati condivisi puo garantire al massimo due delle tre proprieta: Consistency (tutti vedono gli stessi dati), Availability (ogni richiesta riceve risposta), Partition tolerance (funziona nonostante partizioni di rete). La scelta dipende dai requisiti applicativi.

Qual e la differenza fondamentale tra Java RMI e un message-oriented middleware (MOM)?

RMI e basato su distributed object computing: chiamate a metodo sincrone, proxy/stub, trasparenza oggettuale, invocazione di metodi su oggetti remoti. Un MOM (es. RabbitMQ) e basato su messaggi asincroni, code, pubblicazione/sottoscrizione, disaccoppiamento temporale e spaziale. RMI e orientato agli oggetti; i MOM sono orientati ai messaggi.

Descrivi il funzionamento dell'algoritmo di Chandy-Lamport per il global snapshot.

Ogni processo ha un colore (bianco/rosso). Tutti iniziano bianchi. Quando un processo decide di fare lo snapshot (o riceve un marker), salva lo stato locale, diventa rosso, e invia marker su tutti i canali in uscita. I messaggi inviati da processi bianchi e ricevuti da processi rossi costituiscono lo stato dei canali. Canali FIFO garantiscono che nessun processo bianco riceva messaggi da processi rossi, assicurando che gli stati locali siano mutuamente concorrenti (consistent cut).