io.permazen.kv.raft.Consistency Maven / Gradle / Ivy
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.kv.raft;
import io.permazen.kv.RetryKVTransactionException;
/**
* {@link RaftKVTransaction} supported consistency levels.
*
*
* Read-write transactions (i.e., transactions containing any mutations or cluster configuration changes) are always
* {@link #LINEARIZABLE}. Read-only transactions may also have one of the weaker consistency levels. Specifying a
* weaker consistency levels implicitly sets the transaction to be read-only. The default is {@link #LINEARIZABLE}.
*
*
* A transaction's {@link Consistency} is configured when created. To configure a non-{@link #LINEARIZABLE} consistency,
* supply the desired {@link Consistency} under the {@link RaftKVDatabase#OPTION_CONSISTENCY} key in the options provided
* to {@link RaftKVDatabase#createTransaction(java.util.Map)}.
*
*
* Raft {@linkplain RaftKVTransaction#watchKey key watches} are compatible with all {@link Consistency} levels,
* in that if a key watch fires due to a mutation to some key, then a subsequent transaction will see that mutation,
* no matter what {@link Consistency} level is configured for that transaction.
*
* @see RaftKVTransaction#getConsistency RaftKVTransaction.getConsistency()
* @see Strong consistency models
*/
public enum Consistency {
/**
* Uncommitted eventual consistency.
*
*
* This level is only for read-only transactions; read-write transactions are always {@link #LINEARIZABLE}.
*
*
* Transactions see a consistent view of the database that is either already committed or likely to be committed soon.
* This is the same as {@link #EVENTUAL_COMMITTED}, but with the additional (unlikely) possibility that the view that the
* transaction sees may never actually get committed; this can only happen if there is a Raft leadership change
* during the transaction.
*
*
* Transactions at this level require no network communication, commit immediately, and never result in
* {@link RetryKVTransactionException}s. Committing a transaction at this level is actually
* equivalent to invoking {@link RaftKVTransaction#rollback rollback()} instead of {@link RaftKVTransaction#commit commit()}.
*
*
* In Raft terms, transactions are based on the latest uncommitted log entry, the commit operation does not wait
* for that log entry to be committed, and up-to-date reads are not guaranteed.
*/
UNCOMMITTED(false, false, false, true),
/**
* Committed eventual consistency.
*
*
* This level is only for read-only transactions; read-write transactions are always {@link #LINEARIZABLE}.
*
*
* Transactions see a consistent, committed view of the database as it existed at some point in the "recent past".
* The view is not guaranteed to be up-to-date; it's only guaranteed to be as up-to-date as what was known to this
* node when the transaction was opened. In general (for example, if stuck in a network partition minority), the view
* could be from arbitrarily far in the past.
*
*
* Unlike {@link #EVENTUAL}, transactions at this level require no network communication, commit immediately, and never
* result in {@link RetryKVTransactionException}s; however, they see a potentially older view. Compared
* to {@link #UNCOMMITTED}, transactions at this level see a view that is potentially older, in exchange for the guarantee
* that it has definitely been committed.
*
*
* This consistency level is useful when it's important to allow reading the database in any situation, e.g., even when
* the local node is in a minority partition of the cluster. Of course, the data will only be as up-to-date as is known
* locally.
*
*
* In Raft terms, transactions are based on the latest committed log entry, and up-to-date reads are not guaranteed.
*/
EVENTUAL_COMMITTED(true, false, false, true),
/**
* Eventual consistency.
*
*
* This level is only for read-only transactions; read-write transactions are always {@link #LINEARIZABLE}.
*
*
* Transactions see a consistent, committed view of the database as it existed at some point in the "recent past".
* The view is not guaranteed to be up-to-date; it's only guaranteed to be as up-to-date as what was known to this
* node when the transaction was opened. In general (for example, if stuck in a network partition minority), the view
* could be from arbitrarily far in the past.
*
*
* No network traffic is generated by {@link RaftKVTransaction#commit commit()}, and transactions can never throw
* {@link RetryKVTransactionException}s due to read/write conflicts, however they can throw
* {@link RetryKVTransactionException}s in other situations, e.g., if a leader changes occurs and the
* base log entry is never committed.
*
*
* A {@link RaftKVTransaction#commit commit()} blocks until the log entry on which the transaction is based is committed;
* if no concurrent write transactions are occurring, this will happen immediately.
*
*
* In Raft terms, transactions are based on the latest uncommitted log entry, the commit operation waits
* for that log entry to be committed, and up-to-date reads are not guaranteed.
*/
EVENTUAL(false, true, false, true),
/**
* Linearizable consistency.
*
*
* Transactions see a database that evolves step-wise through a global, linear sequence of atomic changes, where
* each change appears to have occurred instantaneously at some point in time between the start and end of the
* corresponding transaction.
*
*
* This is the default consistency level.
*
*
* In Raft terms, transactions are based on the latest uncommitted log entry, the commit operation waits
* for that log entry to be committed, and up-to-date reads are guaranteed.
*/
LINEARIZABLE(false, true, true, false);
private final boolean basedOnCommittedLogEntry;
private final boolean waitsForLogEntryToBeCommitted;
private final boolean guaranteesUpToDateReads;
private final boolean readOnly;
Consistency(boolean basedOnCommittedLogEntry,
boolean waitsForLogEntryToBeCommitted, boolean guaranteesUpToDateReads, boolean readOnly) {
this.basedOnCommittedLogEntry = basedOnCommittedLogEntry;
this.waitsForLogEntryToBeCommitted = waitsForLogEntryToBeCommitted;
this.guaranteesUpToDateReads = guaranteesUpToDateReads;
this.readOnly = readOnly;
}
/**
* Determines the log entry on which transactions at this level are based.
*
*
* When false, transactions are based on this node's most recent log entry, whether committed or not.
* When true, transactions are based on this node's most recent committed log entry. The former provides
* a more up-to-date view, but {@link RaftKVTransaction#commit commit()} can block (and could possibly
* fail with a {@link RetryKVTransactionException}) when {@link #isWaitsForLogEntryToBeCommitted} is true.
* The latter, when combined with {@link #isGuaranteesUpToDateReads} being false, allows read-only transactions
* to commit immediately with no network traffic, even when in a minority partition.
*
* @return true if transactions at this level are always based on committed log entries
*/
public boolean isBasedOnCommittedLogEntry() {
return this.basedOnCommittedLogEntry;
}
/**
* Determines whether transactions at this level block in {@link RaftKVTransaction#commit commit()} until
* the log entry on which they are based is committed.
*
*
* This setting has no effect if {@link #isBasedOnCommittedLogEntry} returns true (i.e., {@link #EVENTUAL_COMMITTED}),
* because in that case the log entry on which the transaction is based is already committed when transaction starts.
*
* @return true if transactions at this level wait for their base log entry to be committed
*/
public boolean isWaitsForLogEntryToBeCommitted() {
return this.waitsForLogEntryToBeCommitted;
}
/**
* Determines whether transactions at this level guarantee reading the most up-to-date information.
*
*
* This setting has no effect on transactions that contain mutations; they always read the most up-to-date information.
*
*
* If this returns false, read-only transactions may read out-of-date information ("eventual consistency").
*
* @return true if transactions at this level guarantee up-to-date reads
*/
public boolean isGuaranteesUpToDateReads() {
return this.guaranteesUpToDateReads;
}
/**
* Determines whether transactions at this level are always read-only.
*
*
* This is true for all levels except {@link #LINEARIZABLE}.
*
* @return true if transactions at this level are always read-only
*/
public boolean isReadOnly() {
return this.readOnly;
}
}