Provides a standalone implementation of the
Raft consensus algorithm.
To create a new server, use the server
CopycatServer.Builder. Servers require
cluster membership information in order to perform communication. Each server must be provided a local
Addressto which to bind the internal
io.atomix.catalyst.transport.Server and a set of addresses for other members in
the cluster.
State machines
Underlying each server is a
StateMachine. The state machine is responsible for maintaining the state with
relation to
Commands and
Querys submitted to the server by a client. State machines are provided
in a factory to allow servers to transition between stateful and stateless states.
Address address = new Address("123.456.789.0", 5000);
Server state machines are responsible for registering
Commands which can be submitted to the cluster. Raft
relies upon determinism to ensure consistency throughout the cluster, so
it is imperative that each server in
a cluster have the same state machine with the same commands. State machines are provided to the server as
a
Supplier to allow servers to
Member#promote(Member.Type) between stateful
and stateless states.
Transports
By default, the server will use the
NettyTransport for communication. You can configure the transport via
CopycatServer.Builder#withTransport(Transport). To use the Netty transport, ensure you have the
io.atomix.catalyst:catalyst-netty jar on your classpath.
CopycatServer server = CopycatServer.builder(address)
Storage
As
Commands are received by the server, they're written to the Raft
Log and replicated to other members
of the cluster. By default, the log is stored on disk, but users can override the default
Storage configuration
via
CopycatServer.Builder#withStorage(Storage). Most notably, to configure the storage module to store entries in
memory instead of disk, configure the
StorageLevel.
CopycatServer server = CopycatServer.builder(address)
Servers use the
Storage object to manage the storage of cluster configurations, voting information, and
state machine snapshots in addition to logs. See the
Storage documentation for more information.
Serialization
All serialization is performed with a Catalyst
Serializer. The serializer is shared across all components of
the server. Users are responsible for ensuring that
Command and
Query submitted to the
cluster can be serialized by the server serializer by registering serializable types as necessary.
By default, the server serializer does not allow arbitrary classes to be serialized due to security concerns. However,
users can enable arbitrary class serialization by disabling the
Serializer#disableWhitelist()on the Catalyst
Serializer:
server.serializer().disableWhitelist();
However, for more efficient serialization, users should explicitly register serializable classes and binary
io.atomix.catalyst.serializer.TypeSerializer. Explicit registration of serializable types allows
types to be serialized using more compact 8- 16- 24- and 32-bit serialization IDs rather than serializing complete
class names. Thus, serializable type registration is strongly recommended for production systems.
server.serializer().register(MySerializable.class, 123, MySerializableSerializer.class);
Bootstrapping the cluster
Once a server has been built, it must either be
#bootstrap() to form a new cluster or
#join(Address...) to an existing cluster. The simplest way to bootstrap a new cluster is to bootstrap
a single server to which additional servers can be joined.
CompletableFuture future = server.bootstrap(););
}
Alternatively, the bootstrapped cluster can include multiple servers by providing an initial configuration to the
#bootstrap(Address...) method on each server. When bootstrapping a multi-node cluster, the bootstrap configuration
must be identical on all servers for safety.
List cluster = Arrays.asList();
}
Adding a server to an existing cluster
Once a single- or multi-node cluster has been
#bootstrap(), often times users need to
add additional servers to the cluster. For example, some users prefer to bootstrap a single-node cluster and
add additional nodes to that server. Servers can join existing bootstrapped clusters using the
#join(Address...)method. When joining an existing cluster, the server simply needs to specify at least one reachable server in the
existing cluster.
CopycatServer server = CopycatServer.builder(new Address("123.456.789.3", 5000)));
}
Server types
Servers form new clusters and join existing clusters as active Raft voting members by default. However, for
large deployments Copycat also supports alternative types of nodes which are configured by setting the server
Member.Type. For example, the
Member.Type#PASSIVE server type does not participate
directly in the Raft consensus algorithm and instead receives state changes via an asynchronous gossip protocol.
This allows passive members to scale sequential reads beyond the typical three- or five-node Raft cluster. The
Member.Type#RESERVE server type is a stateless server that can act as a standby to the stateful
servers in the cluster, being
Member#promote(Member.Type) to a stateful state when necessary.
Server types are defined in the server builder simply by passing the initial
Member.Type to the
Builder#withType(Member.Type) setter:
CopycatServer server = CopycatServer.builder(address)