Class Paxos
- java.lang.Object
-
- org.apache.cassandra.service.paxos.Paxos
-
public class Paxos extends java.lang.ObjectThis class serves as an entry-point to Cassandra's implementation of Paxos Consensus. Note that Cassandra does not utilise the distinguished proposer (Multi Paxos) optimisation; each operation executes its own instance of Paxos Consensus. Instead Cassandra employs various optimisations to reduce the overhead of operations. This may lead to higher throughput and lower overhead read operations, at the expense of contention during mixed or write-heavy workloads. Firstly, note that we do not follow Lamport's formulation, instead following the more common approach in literature (see e.g. Dr. Heidi Howard's dissertation) of permitting any acceptor to vote on a proposal, not only those who issued a promise.
No Commit of Empty Proposals
If a proposal is empty, there can be no effect to the state, so once this empty proposal has poisoned any earlier proposal it is safe to stop processing. An empty proposal effectively scrubs the instance of consensus being performed once it has reached a quorum, as no earlier incomplete proposal (that may perhaps have reached a minority) may now be completed.
Fast Read / Failed Write
This optimisation relies on every voter having no incomplete promises, i.e. their commit register must be greater than or equal to their promise and proposal registers (or there must be such an empty proposal). Since the operation we are performing must invalidate any nascent operation that has reached a minority, and will itself be invalidated by any newer write it might race with, we are only concerned about operations that might be in-flight and incomplete. If we reach a quorum without any incomplete proposal, we prevent any incomplete proposal that might have come before us from being committed, and so are correctly ordered.
NOTE: we could likely weaken this further, permitting a fast operation if we witness a stale incomplete operation on one or more of the replicas, so long as we witness _some_ response that had knowledge of that operation's decision, however we might waste more time performing optimistic reads (which we skip if we witness any in progress promise)
Read Commutativity Optimisation
We separate read and write promises into distinct registers. Since reads are commutative they do not need to be ordered with respect to each other, so read promises consult only the write promise register to find competing operations, whereas writes consult both read and write registers. This permits better utilisation of the Fast Read optimisation, permitting arbitrarily many fast reads to execute concurrently.
A read will use its promise to finish any in progress write it encounters, but note that this is safe for multiple reads to attempt simultaneously. If a write operation has not reached a quorum of promises then it has no effect, so while some read operations may attempt to complete it and others may not, the operation will only be invalidated and these actions will be equivalent. If the write had reached a quorum of promises then every reads will attempt to complete the write. At the accept phase, only the most recent read promise will be accepted so whether the write proposal had reached a quorum or not, a consistent outcome will result.
Reproposal Avoidance
It can occur that two (or more) commands begin competing to re-propose the same incomplete command even after it has already committed - this can occur when an in progress command that has reached the commit condition (but not yet committed) is encountered by a promise, so that it is re-proposed. If the original coordinator does not fail this original command will be committed normally, but the re-proposal can take on a life of its own, and become contended and re-proposed indefinitely. By having reproposals use the original proposal ballot's timestamp we spot this situation and consider re-proposals of a command we have seen committed to be (in effect) empty proposals.
Durability of Asynchronous Commit
To permit asynchronous commit (and also because we should) we ensure commits are durable once a proposal has been accepted by a majority. Replicas track commands that have *locally* been witnessed but not committed. They may clear this log by performing a round of Paxos Repair for each key in the log (which is simply a round of Paxos that tries not to interfere with future rounds of Paxos, while aiming to complete any earlier incomplete round). By selecting some quorum of replicas for a range to perform this operation on, once successful we guarantee that any transaction that had previously been accepted by a majority has been committed, and any transaction that had been previously witnessed by a majority has been either committed or invalidated. To ensure durability across range movements, once a joining node becomes pending such a coordinated paxos repair is performed prior to performing bootstrap, so that commands initiated before joining will either be bootstrapped or completed by paxos repair to be committed to a majority that includes the new node in its calculations, and commands initiated after will anyway do so due to being pending. Finally, for greater guarantees across range movements despite the uncertainty of gossip, paxos operations validate ring information with each other while seeking a quorum of promises. Any inconsistency is resolved by synchronising gossip state between the coordinator and the peers in question.Clearing of Paxos State
Coordinated paxos repairs as described above are preceded by an preparation step that determines a ballot below which we agree to reject new promises. By deciding and disseminating this point prior to performing a coordinated paxos repair, once complete we have ensured that all commands with a lower ballot are either committed or invalidated, and so we are then able to disseminate this ballot as a bound below which may expunge all data for the range. For consistency of execution coordinators seek this latter ballot bound from each replica and, using the maximum of these, ignore all data received associated with ballots lower than this bound.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description static interfacePaxos.Async<Result>
-
Constructor Summary
Constructors Constructor Description Paxos()
-
Method Summary
All Methods Static Methods Concrete Methods Modifier and Type Method Description static BallotballotForConsistency(long whenInMicros, ConsistencyLevel consistency)Create a ballot uuid with the consistency level encoded in the timestamp.static RowIteratorcas(DecoratedKey key, CASRequest request, ConsistencyLevel consistencyForConsensus, ConsistencyLevel consistencyForCommit, ClientState clientState)Apply @param updates if and only if the current values in the row for @param key match the provided @param conditions.static RowIteratorcas(DecoratedKey key, CASRequest request, ConsistencyLevel consistencyForConsensus, ConsistencyLevel consistencyForCommit, ClientState clientState, long proposeDeadline, long commitDeadline)static ConsistencyLevelconsistency(Ballot ballot)static voidevictHungRepairs()static Config.PaxosVariantgetPaxosVariant()static booleanisInRangeAndShouldProcess(InetAddressAndPort from, DecoratedKey key, TableMetadata table, boolean includesRead)static booleanisLinearizable()static BallotnewBallot(Ballot minimumBallot, ConsistencyLevel consistency)static PartitionIteratorread(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, long deadline)static PartitionIteratorread(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, Dispatcher.RequestTime requestTime)static voidsetPaxosVariant(Config.PaxosVariant paxosVariant)static booleanuseV2()
-
-
-
Method Detail
-
cas
public static RowIterator cas(DecoratedKey key, CASRequest request, ConsistencyLevel consistencyForConsensus, ConsistencyLevel consistencyForCommit, ClientState clientState) throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException
Apply @param updates if and only if the current values in the row for @param key match the provided @param conditions. The algorithm is "raw" Paxos: that is, Paxos minus leader election -- any node in the cluster may propose changes for any partition. The Paxos electorate consists only of the replicas for the partition key. We expect performance to be reasonable, but CAS is still intended to be used "when you really need it," not for all your updates. There are three phases to Paxos: 1. Prepare: the coordinator generates a ballot (Ballot in our case) and asks replicas to - promise not to accept updates from older ballots and - tell us about the latest ballots it has already _promised_, _accepted_, or _committed_ - reads the necessary data to evaluate our CAS condition 2. Propose: if a majority of replicas reply, the coordinator asks replicas to accept the value of the highest proposal ballot it heard about, or a new value if no in-progress proposals were reported. 3. Commit (Learn): if a majority of replicas acknowledge the accept request, we can commit the new value. Commit procedure is not covered in "Paxos Made Simple," and only briefly mentioned in "Paxos Made Live," so here is our approach: 3a. The coordinator sends a commit message to all replicas with the ballot and value. 3b. Because of 1-2, this will be the highest-seen commit ballot. The replicas will note that, and send it with subsequent promise replies. This allows us to discard acceptance records for successfully committed replicas, without allowing incomplete proposals to commit erroneously later on. Note that since we are performing a CAS rather than a simple update, when nodes respond positively to Prepare, they include read response of commited values that will be reconciled on the coordinator and checked against CAS precondition between the prepare and accept phases. This gives us a slightly longer window for another coordinator to come along and trump our own promise with a newer one but is otherwise safe. Any successful prepare phase yielding a read that rejects the condition must be followed by the proposal of an empty update, to ensure the evaluation of the condition is linearized with respect to other reads and writes.- Parameters:
key- the row key for the row to CASrequest- the conditions for the CAS to apply as well as the update to perform if the conditions hold.consistencyForConsensus- the consistency for the paxos prepare and propose round. This can only be either SERIAL or LOCAL_SERIAL.consistencyForCommit- the consistency for write done during the commit phase. This can be anything, except SERIAL or LOCAL_SERIAL.- Returns:
- null if the operation succeeds in updating the row, or the current values corresponding to conditions. (since, if the CAS doesn't succeed, it means the current value do not match the conditions).
- Throws:
UnavailableExceptionIsBootstrappingExceptionRequestFailureExceptionRequestTimeoutExceptionInvalidRequestException
-
cas
public static RowIterator cas(DecoratedKey key, CASRequest request, ConsistencyLevel consistencyForConsensus, ConsistencyLevel consistencyForCommit, ClientState clientState, long proposeDeadline, long commitDeadline) throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException
-
read
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, Dispatcher.RequestTime requestTime) throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
-
read
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, long deadline) throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
-
isInRangeAndShouldProcess
public static boolean isInRangeAndShouldProcess(InetAddressAndPort from, DecoratedKey key, TableMetadata table, boolean includesRead)
-
newBallot
public static Ballot newBallot(@Nullable Ballot minimumBallot, ConsistencyLevel consistency)
-
ballotForConsistency
public static Ballot ballotForConsistency(long whenInMicros, ConsistencyLevel consistency)
Create a ballot uuid with the consistency level encoded in the timestamp. UUIDGen.getRandomTimeUUIDFromMicros timestamps are always a multiple of 10, so we add a 1 or 2 to indicate the consistency level of the operation. This should have no effect in practice (except preferring a serial operation over a local serial if there's a timestamp collision), but lets us avoid adding CL to the paxos table and messages, which should make backcompat easier if a different solution is committed upstream.
-
consistency
public static ConsistencyLevel consistency(Ballot ballot)
-
useV2
public static boolean useV2()
-
isLinearizable
public static boolean isLinearizable()
-
setPaxosVariant
public static void setPaxosVariant(Config.PaxosVariant paxosVariant)
-
getPaxosVariant
public static Config.PaxosVariant getPaxosVariant()
-
evictHungRepairs
public static void evictHungRepairs()
-
-