public class Theseus
extends java.lang.Object
| Modifier and Type | Class and Description |
|---|---|
static interface |
Theseus.LongLivedLock
This holds a lock that is expected to live longer than the duration of a single method call.
|
| Modifier and Type | Field and Description |
|---|---|
RaftLifecycle |
lifecycle
The RaftLifecycle that governs this Theseus
|
EloquentRaftNode |
node
The actual Raft node.
|
java.lang.String |
serverName
The name of our node.
|
| Constructor and Description |
|---|
Theseus(int targetQuorumSize)
Create a new dynamically resizing Raft cluster, with the given number
of nodes as the target quorum size.
|
Theseus(RaftAlgorithm algo,
RaftTransport transport,
RaftLifecycle lifecycle)
The constructor takes three arguments: a cluster name (for discovery), a server name (for identifying ourselves,
must be unique within the cluster), and a reference to the lifecycle object that governs this Theseus (so that
tests can pass different RaftLifecycle objects to different Raft instances).
|
Theseus(java.lang.String serverName,
java.util.Collection<java.lang.String> quorum)
Create a Raft cluster with a fixed quorum.
|
Theseus(java.lang.String serverName,
RaftTransport transport,
java.util.Collection<java.lang.String> initialMembership,
RaftLifecycle lifecycle)
Create a new fixed-size raft with the default algorithm, using the given transport.
|
Theseus(java.lang.String serverName,
RaftTransport transport,
int targetClusterSize,
RaftLifecycle lifecycle)
Create a new auto-resizing raft with the default algorithm, using the given transport.
|
| Modifier and Type | Method and Description |
|---|---|
void |
addChangeListener(KeyValueStateMachine.ChangeListener changeListener)
This registers a listener that will be called whenever the key-value store changes.
|
void |
addErrorListener(RaftErrorListener errorListener)
Raft keeps track of an additional error listener
Errors are thrown from
KeyValueStateMachine and EloquentRaftNode by default but
users are free to attach their own error listeners
Usage:
RaftErrorHandler errorListener = (debugMessage, stackTrace) -> {
// Do something with the debug message / stackTrace
// Eg. |
boolean |
bootstrap()
Bootstrap this cluster, if there are no leaders.
|
boolean |
bootstrap(boolean force)
Bootstrap this cluster, if there are no leaders.
|
void |
clearErrorListeners()
Remove all error listeners from Raft.
|
void |
close()
Stop this raft node.
|
java.util.List<java.lang.String> |
errors()
Return any errors Raft has encountered.
|
java.util.Set<java.lang.String> |
getConfiguration()
This returns the current understanding of the cluster membership on this node.
|
java.util.Optional<byte[]> |
getElement(java.lang.String elementName)
This grabs the current state of an element, if it's present in the system.
|
java.util.Collection<java.lang.String> |
getKeys()
This returns the current keys in the state machine.
|
java.util.Map<java.lang.String,java.lang.String> |
getLocks()
Get the set of locks that are held by the state machine, and the server that holds them.
|
java.util.Map<java.lang.String,byte[]> |
getMap()
This returns a snapshot of the current values in the state machine.
|
boolean |
isLeader()
If true, this node is the leader of the Raft cluster.
|
void |
registerFailsafe(RaftFailsafe failsafe)
Register a new failsafe to run occasionally on this raft node.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
releaseLock(java.lang.String lockName)
Release a Raft lock
|
void |
removeChangeListener(KeyValueStateMachine.ChangeListener changeListener)
This removes a listener that will be called whenever the key-value store changes.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
removeElementAsync(java.lang.String elementName,
java.time.Duration timeout)
This removes an element from the Raft key-value store.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
removeElementsAsync(java.util.Set<java.lang.String> elementName,
java.time.Duration timeout)
This removes a set of elements from the Raft key-value store.
|
void |
removeErrorListener(RaftErrorListener errorListener)
Remove an error listener from Raft.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
setElementAsync(java.lang.String elementName,
byte[] value,
boolean permanent,
java.time.Duration timeout)
THIS IS DANGEROUS TO USE! People can clobber each other's writes, and there are tons of race conditions if you use
this call in conjunction with getElement() with no outside synchronization mechanism.
|
RaftState |
state()
Get the current Raft state.
|
java.util.Optional<Theseus.LongLivedLock> |
tryLock(java.lang.String lockName,
java.time.Duration safetyReleaseWindow)
This will try to acquire a lock.
|
java.util.concurrent.CompletableFuture<java.util.Optional<Theseus.LongLivedLock>> |
tryLockAsync(java.lang.String lockName,
java.time.Duration safetyReleaseWindow)
This will try to acquire a lock.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
withDistributedLockAsync(java.lang.String lockName,
java.lang.Runnable runnable) |
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
withDistributedLockAsync(java.lang.String lockName,
java.util.function.Supplier<java.util.concurrent.CompletableFuture<java.lang.Boolean>> runnable)
This runs a function while holding a distributed lock.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
withElementAsync(java.lang.String elementName,
java.util.function.Function<byte[],byte[]> mutator,
java.util.function.Supplier<byte[]> createNew,
boolean permanent)
This is the safest way to do a withElement() update.
|
java.util.concurrent.CompletableFuture<java.lang.Boolean> |
withElementUnlockedAsync(java.lang.String elementName,
java.util.function.Function<byte[],byte[]> mutator,
java.util.function.Supplier<byte[]> createNew,
boolean permanent)
Like
withElementAsync(String, Function, Supplier, boolean), but without the safety of taking the lock on the element beforehand. |
public final java.lang.String serverName
node.EloquentRaftNode.algorithm.EloquentRaftAlgorithm.state.RaftState.serverName.
But, from reading that path, you can tell why there's a helper here.public final EloquentRaftNode node
public final RaftLifecycle lifecycle
public Theseus(RaftAlgorithm algo, RaftTransport transport, RaftLifecycle lifecycle)
algo - The Raft algorithm to use. Defaults to EloquentRaftAlgorithm.transport - The type of transport to use for this Raft cluster.lifecycle - The governing RaftLifecycle for this Theseus, so that we can pass mock ones in inside testspublic Theseus(java.lang.String serverName,
RaftTransport transport,
int targetClusterSize,
RaftLifecycle lifecycle)
serverName - The name of this server in the cluster.transport - The transport to use to communicate with the cluster.targetClusterSize - The target quorum size we try to maintain with auto-resizinglifecycle - The governing RaftLifecycle for this Theseus, so that we can pass mock ones in inside testspublic Theseus(java.lang.String serverName,
RaftTransport transport,
java.util.Collection<java.lang.String> initialMembership,
RaftLifecycle lifecycle)
serverName - The name of this server in the cluster.initialMembership - The initial cluster membership.lifecycle - The governing EloquentLifecycle for this Theseus, so that we can pass mock ones in inside testspublic Theseus(java.lang.String serverName,
java.util.Collection<java.lang.String> quorum)
throws java.io.IOException
serverName - The server name for this Raft node.quorum - The fixed quorum for the cluster.
This is a set of server namesjava.io.IOException - Thrown if we could not create the underlying transport.public Theseus(int targetQuorumSize)
throws java.io.IOException
targetQuorumSize - The target number of nodes in the
quorum.java.io.IOException - Thrown if we could not create the underlying transport.public void close()
public boolean bootstrap(boolean force)
force - If true, attempt to take leadership by force.
That is, massively increase the term number.public boolean bootstrap()
public RaftState state()
public java.util.List<java.lang.String> errors()
public java.util.concurrent.CompletableFuture<java.lang.Boolean> withDistributedLockAsync(java.lang.String lockName,
java.lang.Runnable runnable)
public java.util.concurrent.CompletableFuture<java.lang.Boolean> withDistributedLockAsync(java.lang.String lockName,
java.util.function.Supplier<java.util.concurrent.CompletableFuture<java.lang.Boolean>> runnable)
lockName - the name of the lock -- in a global flat namespacerunnable - the runnable to execute while holding the lockpublic java.util.Optional<Theseus.LongLivedLock> tryLock(java.lang.String lockName, java.time.Duration safetyReleaseWindow)
lockName - the name of the lock to attempt to acquiresafetyReleaseWindow - this is the duration of time after which we will automatically release the lock if it
hasn't already been released. This is just a safety check, you should set it to much
longer than you expect to hold the lock for.public java.util.concurrent.CompletableFuture<java.util.Optional<Theseus.LongLivedLock>> tryLockAsync(java.lang.String lockName, java.time.Duration safetyReleaseWindow)
lockName - the name of the lock to attempt to acquiresafetyReleaseWindow - this is the duration of time after which we will automatically release the lock if it
hasn't already been released. This is just a safety check, you should set it to much
longer than you expect to hold the lock for.public java.util.concurrent.CompletableFuture<java.lang.Boolean> releaseLock(java.lang.String lockName)
lockName - The name of the lock we are releasingpublic java.util.concurrent.CompletableFuture<java.lang.Boolean> withElementAsync(java.lang.String elementName,
java.util.function.Function<byte[],byte[]> mutator,
@Nullable
java.util.function.Supplier<byte[]> createNew,
boolean permanent)
elementName - the name of the element -- in a global flat namespacemutator - the function to call, while holding a lock on the element, to mutate the element (doesn't have to
actually change anything, but can).createNew - a function to supply a new element, if none is present in the map alreadypermanent - if false, this element will be automatically removed when we disconnect from the cluster, if we're
the last people to have edited the element.java.util.NoSuchElementException - if we didn't supply a creator, and the object does not exist in Raft.public java.util.concurrent.CompletableFuture<java.lang.Boolean> withElementUnlockedAsync(java.lang.String elementName,
java.util.function.Function<byte[],byte[]> mutator,
@Nullable
java.util.function.Supplier<byte[]> createNew,
boolean permanent)
withElementAsync(String, Function, Supplier, boolean), but without the safety of taking the lock on the element beforehand.
This is a bit of a dangerous method, as it can open the caller up to race conditions, and should be used sparingly.elementName - the name of the element -- in a global flat namespacemutator - the function to call, while holding a lock on the element, to mutate the element (doesn't have to
actually change anything, but can).createNew - a function to supply a new element, if none is present in the map alreadypermanent - if false, this element will be automatically removed when we disconnect from the cluster, if we're
the last people to have edited the element.java.util.NoSuchElementException - if we didn't supply a creator, and the object does not exist in Raft.public java.util.concurrent.CompletableFuture<java.lang.Boolean> setElementAsync(java.lang.String elementName,
byte[] value,
boolean permanent,
java.time.Duration timeout)
elementName - the name of the element to writevalue - the value to set the element topermanent - if false, the element will be automatically removed when we disconnect from the cluster, if we're
the last people to have edited the element. If true, the element will stick around forever.public java.util.concurrent.CompletableFuture<java.lang.Boolean> removeElementAsync(java.lang.String elementName,
java.time.Duration timeout)
elementName - the name of the element to removepublic java.util.concurrent.CompletableFuture<java.lang.Boolean> removeElementsAsync(java.util.Set<java.lang.String> elementName,
java.time.Duration timeout)
elementName - the name of the element to removepublic java.util.Optional<byte[]> getElement(java.lang.String elementName)
elementName - the name of the element -- in a global flat namespacepublic java.util.Set<java.lang.String> getConfiguration()
public java.util.Map<java.lang.String,byte[]> getMap()
public java.util.Collection<java.lang.String> getKeys()
public java.util.Map<java.lang.String,java.lang.String> getLocks()
public void addChangeListener(KeyValueStateMachine.ChangeListener changeListener)
changeListener - the listener to registerpublic void removeChangeListener(KeyValueStateMachine.ChangeListener changeListener)
changeListener - the listener to deregisterpublic void addErrorListener(RaftErrorListener errorListener)
KeyValueStateMachine and EloquentRaftNode by default but
users are free to attach their own error listeners
Usage:
RaftErrorHandler errorListener = (debugMessage, stackTrace) -> {
// Do something with the debug message / stackTrace
// Eg. Logging, or alerting via PagerDuty
}
addErrorListener(errorListener);
// Later in the code where there is an error
throwRaftError(incident_key, debug_message);errorListener - The error listener to add.public void removeErrorListener(RaftErrorListener errorListener)
errorListener - The error listener to remove.public void clearErrorListeners()
public boolean isLeader()
public void registerFailsafe(RaftFailsafe failsafe)
failsafe - the one to register