public interface RaftService extends Snapshottable, RaftSessionListener
Users should extend this class to create a state machine for use within a RaftServer.
State machines are responsible for handling operations submitted to the Raft cluster and
filtering committed operations out of the Raft log. The most important rule of state machines is
that state machines must be deterministic in order to maintain Raft's consistency guarantees. That is,
state machines must not change their behavior based on external influences and have no side effects. Users should
never use System time to control behavior within a state machine.
When commands and queries
(i.e. operations) are submitted to the Raft cluster, the RaftServer will log and replicate them as
necessary and, once complete, apply them to the configured state machine.
#configure(RaftServiceExecutor)
method. Each operation method must take a single Commit argument for a specific operation type.
public class MapStateMachine extends StateMachine {
public Object put(Commit<Put> commit) {
Commit<Put> previous = map.put(commit.operation().key(), commit);
if (previous != null) {
try {
return previous.operation().value();
} finally {
previous.close();
}
}
return null;
}
public Object get(Commit<Get> commit) {
try {
Commit<Put> current = map.get(commit.operation().key());
return current != null ? current.operation().value() : null;
} finally {
commit.close();
}
}
}
When operations are applied to the state machine they're wrapped in a Commit object. The commit provides the
context of how the command or query was committed to the cluster, including the log Commit.index(), the
RaftSession from which the operation was submitted, and the approximate
wall-clock Commit.wallClockTime() at which the commit was written to the Raft log. Note that the commit time is
guaranteed to progress monotonically, but it may not be representative of the progress of actual time. See the
Commit documentation for more information.
State machine operations are guaranteed to be executed in the order in which they were submitted by the client,
always in the same thread, and thus always sequentially. State machines do not need to be thread safe, but they must
be deterministic. That is, state machines are guaranteed to see io.atomix.protocols.raft.RaftCommands in the
same order on all servers, and given the same commands in the same order, all servers' state machines should arrive at
the same state with the same output (return value). The return value of each operation callback is the response value
that will be sent back to the client.
RaftServiceExecutor is responsible for executing state machine operations sequentially and provides an
interface similar to that of ScheduledExecutorService to allow state machines to schedule
time-based callbacks. Because of the determinism requirement, scheduled callbacks are guaranteed to be executed
deterministically as well. The executor can be accessed via the #executor field.
See the RaftServiceExecutor documentation for more information.
public void putWithTtl(Commit<PutWithTtl> commit) {
map.put(commit.operation().key(), commit);
executor.schedule(Duration.ofMillis(commit.operation().ttl()), () -> {
map.remove(commit.operation().key()).close();
});
}
During command or scheduled callbacks, RaftSessions can be used to send state machine events back to the client.
For instance, a lock state machine might use a client's RaftSession
to send a lock event to the client.
public void unlock(Commit<Unlock> commit) {
try {
Commit<Lock> next = queue.poll();
if (next != null) {
next.session().publish("lock");
}
} finally {
commit.close();
}
}
Attempts to publish
events during the execution will result in an IllegalStateException.
Even though state machines on multiple servers may appear to publish the same event, Raft's protocol ensures that only one server ever actually sends the event. Still, it's critical that all state machines publish all events to ensure consistency and fault tolerance. In the event that a server fails after publishing a session event, the client will transparently reconnect to another server and retrieve lost event messages.
index). To support snapshotting, state machine implementations should implement
the Snapshottable interface.
public class ValueStateMachine extends StateMachine implements Snapshottable {
private Object value;
public void set(Commit<SetValue> commit) {
this.value = commit.operation().value();
commit.close();
}
public void snapshot(SnapshotWriter writer) {
writer.writeObject(value);
}
}
For snapshottable state machines, Raft will periodically request a Snapshot
of the state machine's state by calling the Snapshottable.snapshot(io.atomix.protocols.raft.storage.snapshot.SnapshotWriter)
method. Once the state machine has written a snapshot of its state, Raft will automatically remove all commands
associated with the state machine from the underlying log.Commit,
ServiceContext,
RaftServiceExecutor| Modifier and Type | Method and Description |
|---|---|
byte[] |
apply(Commit<byte[]> commit)
Applies a commit to the state machine.
|
default void |
close()
Closes the state machine.
|
void |
init(ServiceContext context)
Initializes the state machine.
|
install, snapshotonClose, onExpire, onOpenvoid init(ServiceContext context)
context - The state machine context.NullPointerException - if context is nullbyte[] apply(Commit<byte[]> commit)
commit - the commit to applydefault void close()
Copyright © 2013–2017. All rights reserved.