Sodacan Configuration

The Sodacan configuration can start with a simple builder which has defaults for everything and then add or override settings from there. A Configuration usually starts like this:

	Config config = new SimpleBuilder()
		// Add more settings from below, here
		.build();

All Sodacan settings are implemented as functions. For example, the Host number is configured as:

	config.hostNumber(c -> 1)

This allows the host number to be determined by, for example, evaluating a system property if desired.

Settings that ultimately return a primitive value also provide a convenience method that just accepts a simple value. For example:

	config.hostNumber(1)

Local vs global: Local settings apply to the host in which it is declared. Global settings are maintained by the Sodacan coordinator. These setting are common to all Hosts and updates are distributed to all Hosts.

Static vs dynamic: Static settings are instantiated once during startup of a Host. Dynamic settings means that the setting's function is applied each time it is called.

The configuration should be considered read only once the Host is started. For certain dynamic settings, synchronization is provided to ensure thread safety.

Actor Group Assigner

Local-Static

Method used to assign a new ActorId to an ActorGroup.

Default is:

	.actorGroupAssigner(c -> new FixedActorGroupAssigner(c,1))

Actor Group Function

Local-Dynamic

A factory Function that creates an ActorGroup. This function is called by Sodacan when the Host determines that a specific ActorGroup should be created. A newly created ActorGroup will contact the Host to determine the operating mode (live, hot, etc) of the ActorGroup.

Default specification is:

	.actorGroup((c,id) -> new DefaultActorGroup(c,id))

Actor Group Replicas

Global-Dynamic

The number of hot/live replicas of each ActorGroup to maintain. The value should be at least 3 for a durable configuration. Avoid even numbers, especially 2, in order to avoid split-brain issues.

If the number of replicas is increased, additional replicas will be created and synchronized in the background.

If the number of replicas is decreased, then the number of hot replicas is reduced but the replicas eliminated from the hot set remain in “warm” backup mode. Note: If a replica is abruptly lost, then that replica is demoted to “cold” backup.

Default is:

	.actorGroupReplicas(c -> 1)

Actor Groups

Global-Static

This specifies the number of ActorGroups among which the ActorGroupAssigner randomly chooses an ActorGroup for a new ActorId. Applies to all ActorGroups. Ths is used by Sodacan.

Default is:

	.actorGroups(config -> 1)

Actor Group Threads

Local-Static

The number of threads allocated to each actor group.

Default is:

	.actorGroupThreads(c -> 20)

If the value is zero, Sodacan will allow any number of threads to be created. Depending on the workload, this can run into the thousands. Performance testing reveals that this approach yields slower throughput than having a limited thread pool size.

Actor Id

Local-Static

A factory function that generates an ActorId.

Note: In Sodacan, Actors do not create new Actors. An Actor creates an ActorId and sends a message to that new ActorId. Sodacan then creates the new Actor which may be on a different Host than the Actor that created the new ActorId. The new Actor can decide if the ActorId is valid based on the contents of the first Message that it receives.

The default is sufficient for testing and demonstration but not production:

	.actorIdFactory((c) -> new SequentialActorIdFactory(c))

This ActorId factory is appropriate for production:

	.actorIdFactory((c) -> new UUIDActorIdFactory(c))

Example of creating a new ActorId:

	ActorId actorId = config.createActorId(TestActor.NAME);

Backpressure Limit

Local-Dynamic

The maximum number of in-flight messages in an actor group before backpressure is applied by blocking when submitting a new inbound message.

Default is:

	.backpressureLimit(c -> 100000)

Clock

Local-Static

Sodacan makes no effort to synchronize clocks between Hosts.

Default is:

	.clock(c -> Clock.systemUTC())

Example of specifying a fixed clock used for testing and debugging:

	.clock(c -> new FixedClock(Instant.parse("2024-12-19T20:00:00Z")))

Convenience method that returns the current instant of the configured clock:

	config.now();

CoordinatorFn

Global-Dynamic

A factory function for creating a Coordinator. This function will be called by Sodacan, usually when constructing a Host.

Default is:

	.coordinator(c -> new SingleHostCoordinator(c))

An alternate configuration using Apache Ratis in the Coordinator

	.coordinator(c -> new RatisCoordinator(c))

Host Factory

Local-Static

A factory function for creating a Sodacan Host. The factory function is static, called just once: Only one Host per Host.

To create a second Host in the same JVM, you must create a new Config object.

Default specification:

	.host(c -> new DefaultHost(c))

Usage example to start a host:

	config.getHost().start();

Usage example to close a host:

	config.getHost().close();

HostNumber

Local-Static

Each Host has a unique number in a Sodacan cluster.

Default is:

	.hostNumber(c -> 1)		

Example of defining a host number from a system property:

	.hostNumber(c -> Integer.parseInt(System.getProperty("localHostNumber"))

Message Id Function

Local-Dynamic

A factory function to create a messageId. This factory is usually called by Sodacan when creating a new message.

Default is:

	.messageId(c -> new DefaultMessageId(c))

Message Function

Local-Dynamic

A factory function to create a new message.

An actor that forwards an inbound message does not call this method. Rather, the forward method calls a copy constructor on the inbound message. So, this method is for a completely new Message only.

Default is:

	.message(c -> new DefaultMessage(c.createMessageId()))

Sample usage:

	Message m = config.createMessage()

Persister Factory

Local-Static

A Factory Function to create a Persister. This is normally called by Sodacan when the persistent fields of an Actor instance need to be read from or written to storage.

Default is a persister that only stores data in memory which is sufficient for testing and demonstration:

	.persisterFactory(c -> new MemoryPersisterFactory(c))

This alternate persister stores data on disk using RocksDB. One DB per ActorGroup:

	.persisterFactory(c -> new RocksPersisterFactory(c, "rocksdb"))

This alternate persister stores data on disk in individual flat files for each individual Actor.

	.persisterFactory(c -> new FilePersisterFactory(c))

Random Function

Local-Static

Function for creating a random number.

	.random(c -> ThreadLocalRandom.current())
	.random(c -> new FixedRandom(1))
	int r = config.getRandom().nextInt();
  1. Default specification

  2. An alternative used for testing

  3. Example of generating a random number

Register Actor Type

Local-Static

Collects metadata for an Actor type and creates a factory which is called by Sodacan to create an Actor instance.

There is no default. To register an ActorType:

	.registerActorType(TestActor.NAME, TestActor.class)

The constructor for TestActor will be called with two parameters: Config and the ActorId of the Actor to create. If the actor's constructor takes different parameters a factory function can also be specified:

	.registerActorType(TestActor.NAME, TestActor.class, (c, aid) -> new MyActor(c, aid))

An individual Actor may be instantiated more than once, each with an Eviction between. The Actor's lifetime is controlled exclusively by the ActorGroup's Scheduler, but never while a message is being processed.

Root Directory

Local-Static

This function specifies the root directory for all files for this Host. Used for persisting Actor State. The default should normally be changed to a non-temporary directory.

Default is specified as:

	.rootDirectory(c -> System.getProperty("java.io.tmpdir"))

Example usage:

	String dir = config.getRootDirectory();

Scheduler Function

Local-Static

A Factory Function to create a Scheduler. This is normally called by Sodacan from the ActorGroup constructor.

Default is:

	.scheduler((c, ag) -> new DefaultScheduler(c,ag))

Serializer Factory

Local-Static

A factory function that returns a serializer for both messages and actors. Serializers are not thread safe and can be expensive to create so the factory usually maintains a thread-safe pool of serializers and loans them out when needed by an Actor.

Default is:

	.serializerFactory(c -> new GsonSerializerFactory(c))

Other serializers include Kryo, Avro, and Fury. Gson is not the fastest but is it pure Json which makes visualizing messages and actors easier.

Shutdown Wait Ms

Local-Dynamic

The number of milliseconds to wait between checks for in-flight messages to be processed before closing an ActorGroup. An ActorGroup cannot be migrated to another Host until it has been shut down.

	.shutdownWaitMs(c -> 200)