Consensus
Paxos is a family of best-effort protocols proposed by L. Lamport for reaching consensus within an asynchronous communications network with unreliable participants. Advanced versions of Paxos can cope with byzantine behaviours.
More details :
Basic Paxos
Basic Paxos is composed of three roles:
- clients : processors that sends request.
- Acceptors : processors that accept values, where a value is chosen if a majority accept.
- Proposers : processors that receive the user's request, and who will try to get the consensus of the acceptors.
- Learner : processors that will save the action log, execute it and transmit the response of the request to the user.
Note: An agent can do several roles at the same time.
The paxos process is executed in two phases.
-
The preparation phase.
- The Proposer initializes or increments his id.
- The Proposer sends each Acceptor a Propose(topic, id). id is the largest id seen by the Proposer and topic is the topic of the concensus.
- Acceptors return Promise(topic, id, val_accepted) if the largest id sent is met, otherwise they reject with a Nak(id). val_accepted is the value accepted during a consensus (default is null).
- If the majority has made a promise, we pass to second phase, otherwise we go back to the first step.
-
The acceptance phase.
- The Proposer sends an Accept(topic, id, value) to the Acceptors. value is chosen by Client if all val_accepted are null, otherwise it will be the value val_accepted.
- Acceptors return Accepted(topic, id, value) if the id sent is the largest, Nak(topic, id) otherwise.
- If the acceptance is not successful, we go back to the first phase, otherwise we send the Learner the associated request.
Here is a simple sequence of events.
sequenceDiagram
Client->>Proposer: request(topic, val)
Proposer->>Acceptor1:Propose(topic, 1)
note over Acceptor1: The first Proposer is always accepted.
Proposer->>Acceptor2:Propose(topic, 1)
note over Acceptor2: The first Proposer is always accepted.
Acceptor2->>Proposer:Promise(topic, 1, null)
Acceptor1->>Proposer:Promise(topic, 1, null)
Proposer->>Acceptor1:Accept(topic, 1, val)
Proposer->>Acceptor2:Accept(topic, 1, val)
Acceptor2->>Proposer:Accepted(topic, 1, val)
Acceptor1->>Proposer:Accepted(topic, 1, val)
Proposer->>Leaner:Decide(topic, val)
Leaner->>Client:res
note over Client: res is the answer of the request(topic, val).
Here is a more complex sequence of events.
sequenceDiagram
Client->>Proposer1: request(topic, val1)
Client->>Proposer2: request(topic, val2)
Proposer1->>Acceptor1:Propose(topic, 1)
note over Acceptor1: The first Proposer is always accepted.
Acceptor1->>Proposer1:Promise(topic, 1, null)
Proposer2->>Acceptor1:Propose(topic, 1)
Proposer2->>Acceptor2:Propose(topic, 1)
note over Acceptor2: The first Proposer is always accepted.
Acceptor2->>Proposer2:Promise(topic, 1, null)
Acceptor1->>Proposer2:Nak(topic, 1)
Proposer2->>Acceptor1:Propose(topic, 2)
Proposer1->>Acceptor2:Propose(topic, 1)
Acceptor2->>Proposer1:Nak(topic, 1)
Proposer2->>Acceptor2:Propose(topic, 2)
Acceptor2->>Proposer2:Promise(topic, 2, null)
Proposer2->>Acceptor1:Accept(topic, 2, val2)
Proposer2->>Acceptor2:Accept(topic, 2, val2)
Acceptor2->>Proposer2:Accepted(topic, 2, val2)
Acceptor1->>Proposer2:Accepted(topic, 2, val2)
Proposer2->>Leaner:Decide(topic, val2)
Leaner->>Client:res2
note over Client: res2 is the answer of the request(topic, val2).
note over Proposer1: Continue for the consensus of the request(topic, val1).
Proposer1->>Acceptor1:Propose(topic, 3)
Proposer1->>Acceptor2:Propose(topic, 3)
Acceptor2->>Proposer1:Promise(topic, 3, val2)
Acceptor1->>Proposer1:Promise(topic, 3, val2)
Proposer1->>Acceptor1:Accept(topic, 3, val2)
Proposer1->>Acceptor2:Accept(topic, 3, val2)
Acceptor2->>Proposer1:Accepted(topic, 3, val2)
Acceptor1->>Proposer1:Accepted(topic, 3, val2)
Proposer1->>Leaner:Decide(topic, val2)
Leaner->>Client:res2
note over Client: res2 is the answer of the request(topic, val2).
Multi-Paxos
MultiPaxos has the same roles as BasicPaxos, what changes is that instead of having a consensus for a single value, it is done for a series of values.
The paxos process is executed in two phases.
-
The preparation phase.
- The Proposer initializes or updates his id.
- The Proposer sends each Acceptor a Propose(topic, id). id is the largest id seen by the Proposer and topic is the topic of the concensus.
- Acceptors return Promise(topic, id, list_val_accepted) if the largest id sent is met, otherwise they reject with a Nak(id). list_val_accepted is the list of value accepted during a consensus (default is null).
- If the majority has made a promise, we pass to second phase, otherwise we go back to the first step.
-
The acceptance phase.
- The Proposer sends an Accept(topic, id, list_value) to the Acceptors. value is chosen by Client if all val_accepted are null, otherwise it will be the list of value list_val_accepted where the new value is added.
- Acceptors return Accepted(topic, id, list_value) if the id sent is the largest, Nak(topic, id) otherwise.
- If the acceptance is not successful, we go back to the first phase, otherwise we send the Learner the associated request.
Let's take the previous examples:
sequenceDiagram
Client->>Proposer: request(topic, val)
Proposer->>Acceptor1:Propose(topic, 1)
note over Acceptor1: The first Proposer is always accepted.
Proposer->>Acceptor2:Propose(topic, 1)
note over Acceptor2: The first Proposer is always accepted.
Acceptor2->>Proposer:Promise(topic, 1, null)
Acceptor1->>Proposer:Promise(topic, 1, null)
Proposer->>Acceptor1:Accept(topic, 1, {val})
Proposer->>Acceptor2:Accept(topic, 1, {val})
Acceptor2->>Proposer:Accepted(topic, 1, {val})
Acceptor1->>Proposer:Accepted(topic, 1, {val})
Proposer->>Leaner:Decide(topic, {val})
Leaner->>Client:res
note over Client: res is the answer of the request(topic, val).
Client->>Proposer: request(topic, val2)
Proposer->>Acceptor1:Propose(topic, 2)
note over Acceptor1: The first Proposer is always accepted.
Proposer->>Acceptor2:Propose(topic, 2)
note over Acceptor2: The first Proposer is always accepted.
Acceptor2->>Proposer:Promise(topic, 2, {val})
Acceptor1->>Proposer:Promise(topic, 2, {val})
Proposer->>Acceptor1:Accept(topic, 2, {val, val2})
Proposer->>Acceptor2:Accept(topic, 2, {val, val2})
Acceptor2->>Proposer:Accepted(topic, 2, {val, val2})
Acceptor1->>Proposer:Accepted(topic, 2, {val, val2})
Proposer->>Leaner:Decide(topic, {val, val2})
Leaner->>Client:res2
note over Client: res2 is the answer of the request(topic, val2).
sequenceDiagram
Client->>Proposer1: request(topic, val1)
Client->>Proposer2: request(topic, val2)
Proposer1->>Acceptor1:Propose(topic, 1)
note over Acceptor1: The first Proposer is always accepted.
Acceptor1->>Proposer1:Promise(topic, 1, null)
Proposer2->>Acceptor1:Propose(topic, 1)
Proposer2->>Acceptor2:Propose(topic, 1)
note over Acceptor2: The first Proposer is always accepted.
Acceptor2->>Proposer2:Promise(topic, 1, null)
Acceptor1->>Proposer2:Nak(topic, 1)
Proposer2->>Acceptor1:Propose(topic, 2)
Proposer1->>Acceptor2:Propose(topic, 1)
Acceptor2->>Proposer1:Nak(topic, 1)
Proposer2->>Acceptor2:Propose(topic, 2)
Acceptor2->>Proposer2:Promise(topic, 2, null)
Proposer2->>Acceptor1:Accept(topic, 2, {val2})
Proposer2->>Acceptor2:Accept(topic, 2, {val2})
Acceptor2->>Proposer2:Accepted(topic, 2, {val2})
Acceptor1->>Proposer2:Accepted(topic, 2, {val2})
Proposer2->>Leaner:Decide(topic, {val2})
Leaner->>Client:res2
note over Client: res2 is the answer of the request(topic, val2).
note over Proposer1: Continue for the consensus of the request(topic, val1).
Proposer1->>Acceptor1:Propose(topic, 3)
Proposer1->>Acceptor2:Propose(topic, 3)
Acceptor2->>Proposer1:Promise(topic, 3, {val2})
Acceptor1->>Proposer1:Promise(topic, 3, {val2})
Proposer1->>Acceptor1:Accept(topic, 3, {val2, val1})
Proposer1->>Acceptor2:Accept(topic, 3, {val2, val1})
Acceptor2->>Proposer1:Accepted(topic, 3, {val2, val1})
Acceptor1->>Proposer1:Accepted(topic, 3, {val2, val1})
Proposer1->>Leaner:Decide(topic, {val2, val1})
Leaner->>Client:res1
note over Client: res1 is the answer of the request(topic, val1).