Skip to main content

Modes

A GoAkt actor system runs in one of two modes:
  • Standalone: What it gives you: Single-process actor system; all messaging is in-process. Setup: NewActorSystem + Start, no extra options.
  • Clustered: What it gives you: Multi-node membership: location transparency, distribution, relocation, cluster singletons. Setup: WithRemote + WithCluster.
Remoting can also be enabled without clustering for point-to-point messaging between known nodes. That middle ground (no membership, no registry, no relocation) is covered in Remoting.

Standalone mode

In standalone mode, GoAkt runs in a single process. Actors are spawned under the user guardian and communicate through direct mailbox enqueue. No network or cluster setup is required. Use it for development and testing, single-node deployments, and services that do not need distribution. Limitations:
  • No remoting: actors cannot span multiple processes.
  • No clustering: no discovery, no relocation, no cluster singletons, no grain distribution.
  • Placement options on SpawnOn and GrainOf are ignored; everything runs locally.
  • Single point of failure: a process crash stops everything.

Clustered mode

In clustered mode, multiple nodes form a cluster. Actors and grains are location-transparent: you send to a PID or a grain identity, and the framework routes to the correct node. Three components make that work:
  • Discovery: a pluggable provider (Consul, etcd, Kubernetes, NATS, mDNS, static) that tells the cluster how to find peers. See Service Discovery.
  • Cluster registry: a distributed, partitioned key/value store (built on Olric) that records where every actor and grain lives. See Partition Hashing.
  • Remoting: the TCP transport that carries messages between nodes. See Remoting.
Use it for high availability, horizontal scaling, workload distribution across machines, cluster singletons, and relocation.

Enabling cluster mode

Cluster mode has four hard requirements, all checked at start or by configuration validation:
  1. Remoting: clustering needs remoting; Start fails with clustering needs remoting to be enabled when WithRemote is missing.
  2. A discovery provider, set with WithDiscovery.
  3. A discovery port and a peers port, set with WithDiscoveryPort and WithPeersPort.
  4. At least one registered actor kind or grain kind, set with WithKinds or WithGrains, so remote spawns, activations, and relocations can instantiate workloads on this node.
Enabling cluster mode
Each node listens on three ports:
  • Remoting port: Set with: remote.NewConfig. Carries: Actor and grain messages between nodes.
  • Discovery port: Set with: WithDiscoveryPort. Carries: Gossip and membership traffic.
  • Peers port: Set with: WithPeersPort. Carries: Cluster registry replication.
TLS configured on the actor system with actor.WithTLS applies to both remoting and the cluster transport. See Remoting for certificate requirements.

Configuration reference

NewClusterConfig returns a configuration with working defaults; only the four requirements above must be provided. The options below are grouped by concern.

Workload and topology

  • WithKinds: Registers actor kinds this node can host (remote spawn, relocation). Default: none.
  • WithGrains: Registers grain kinds this node can host (remote activation, recreation, relocation). Default: none.
  • WithRoles: Advertises node roles for role-constrained placement. Duplicates are removed; order is not meaningful. Default: none.
  • WithGrainActivationBarrier: Delays grain activations until the minimum peers quorum is reached, or until d elapses. 0 waits indefinitely. Reduces churn during startup and rolling deployments. Default: disabled.
  • WithDataCenter: Enables the multi-DC control plane. See Multi Datacenter. Default: disabled.
  • WithCRDT: Enables CRDT replication through a Replicator system actor. Without it there is zero CRDT overhead. See Distributed Data. Default: disabled.

Registry replication and storage

The cluster registry is partitioned across nodes and replicated per partition. These knobs control durability, consistency, and memory footprint.
  • WithPartitionCount: Number of registry partitions. Should be a prime number. See Partition Hashing. Default: 271.
  • WithPartitionHasher: Hash mapping names and identities to partitions. Must be identical, deterministic, and stable on every node. Default: xxh3.
  • WithReplicaCount: Copies kept of each partition. The default survives one node loss. Use 3 to tolerate two concurrent losses. Default: 2.
  • WithWriteQuorum: Replicas that must acknowledge a registry write. Must be >= 1 and <= replicaCount. Default: 1.
  • WithReadQuorum: Replicas consulted per registry read. Must be >= 1 and <= replicaCount. Default: 1.
  • WithMinimumPeersQuorum: Members a node must see in its own membership view before the registry serves it: at startup, when the registry map is created, before every registry request received from another node, and before the leader recomputes the routing table. Also the quorum the grain activation barrier waits for. GoAkt has no split-brain resolver, so this setting is the only partition safeguard. With the default of 1, each side of a network split keeps serving, takes its own oldest member as leader, sees the other side’s nodes as departed and relocates their actors, so an actor or a singleton can run on both sides until the split heals. A value above half the cluster size keeps the minority side from recomputing its routing table and from serving registry requests to its peers, so it cannot relocate; it does not stop the minority’s already-running actors. Keep it <= replicaCount. Default: 1.
  • WithTableSize: In-memory size of the registry key/value store on this node. Default: 4 MB.
  • WithStoreDir: Directory for the node’s cluster store, the BoltDB file that holds peer state while the node runs. The file is created on start and deleted on stop, so an ephemeral writable volume (for example an emptyDir) is enough. Set it when the home directory is not writable, such as a pod with a read-only root filesystem or a sandbox without $HOME. Default: ~/.goakt/cluster.
WithReplicaCount(1) keeps no backups: registry partitions owned by a crashed node are lost with it, crash recovery silently misses the affected records, and the actor system logs a startup warning. Keep the default of 2 or higher in production.
The classic read-your-writes guideline (readQuorum + writeQuorum > replicaCount) is not enforced, and the default (replicaCount=2 with both quorums at 1) deliberately does not follow it: the registry is rebuildable, single activation is arbitrated by the partition’s primary owner rather than by quorum overlap, and quorums of 1 keep registry writes succeeding while a node is down. For a majority configuration, use replicaCount=3 with writeQuorum=2 and readQuorum=2, accepting higher write latency and fan-out.

Timing

  • WithWriteTimeout: Maximum duration of a registry write before it fails. Default: 1s.
  • WithReadTimeout: Maximum duration of a registry read before it fails. Default: 1s.
  • WithBootstrapTimeout: Maximum wait for cluster formation during Start; the system fails to start when it elapses. Default: 10s.
  • WithShutdownTimeout: Maximum duration of a graceful cluster shutdown. Default: 3m.
  • WithClusterStateSyncInterval: How often nodes synchronize routing tables. Keep it greater than the write timeout. Default: 1m.
  • WithClusterBalancerInterval: How often the balancer evaluates placement and acknowledges rebalance epochs. Keep it shorter than the state sync interval so epochs do not stack. Default: 1s.
  • WithNetworkProfile: Describes the network the cluster nodes share and tunes failure detection for it. NetworkProfileLAN (default) is for nodes in one data center or availability zone. NetworkProfileLocal is for nodes on one host or a low-latency test cluster; it confirms failures fastest and is the most likely to mistake a long pause for a crash. NetworkProfileWAN is for nodes spread across regions or reached over the public internet; it tolerates latency and loss and confirms failures slowly.
  • WithConvergenceTimeout: Bounds how long the cluster waits, after it confirmed that a node joined or left, for every node to route on the same membership before it publishes NodeJoined or NodeLeft anyway. Default: 10s. This is not the failure confirmation time, which the network profile sets and which ends before this wait starts; it bounds the convergence that follows, normally under a second and up to a few seconds when the departed node was the oldest member. A shorter bound does not make failures visible sooner and can publish the event while a node still routes to the departed one; to detect failures sooner, pick a faster network profile.
Recommended starting points: small and medium clusters run well with a 1s to 5s balancer interval and 30s to 1m state sync; large or busy clusters should increase both together (for example 5s balancer with 1m to 2m state sync).

Failure detection

When a node stops abruptly, the survivors confirm its loss after several missed probes, then converge on the new membership, then publish NodeLeft and relocate its actors. Confirmation time depends on the network profile and grows slowly with the cluster size; convergence normally takes under a second; relocation starts about three seconds later, once the cluster store has settled. Expected time from the crash to NodeLeft: Faster confirmation means a higher chance that a garbage collection pause or CPU throttling is mistaken for a crash, which relocates a healthy node’s actors for nothing. Keep the default unless the deployment matches another profile.
With TLS enabled, the Local and LAN rows are about 11s, 15s and 19s.

Registry consistency

Actor and grain creation is synchronous with respect to the cluster registry: Spawn, SpawnOn, SpawnSingleton, and grain activation only return once the registry record is written to the cluster store. As soon as a creation call succeeds, the actor is resolvable by name, or by qualified name (parent/child, built with actor.QualifiedName) for a child, for example via ActorOf, and reachable from any node in the cluster, with no propagation window to retry around. When the registry write fails, the creation call returns the error and the actor or grain is stopped, so a failed creation leaves nothing behind.

Placement

Where a new actor or grain lands is controlled at spawn or activation time through placement strategies (round robin, random, local, least load) and node roles. See Placement for the strategies, role constraints, and defaults for both actors and grains.

Relocation

When a node leaves, the leader relocates its actors and grains to remaining nodes. Singleton actors move to the leader; others are distributed. Actors can opt out of relocation via spawn options. See Relocation for the full flow, configuration, and relocatability requirements.

Leadership

Exactly one node in the cluster acts as the coordinator (leader) at any given time. The leader drives internal responsibilities such as relocation and cluster singleton placement, but its status is also queryable from application code:
  • ActorSystem.IsLeader: Reports whether the local node is the current cluster coordinator.
  • ActorSystem.Leader: Returns the *remote.Peer describing the current cluster leader.
Both methods require cluster mode; they return ErrClusterDisabled when clustering is not enabled.
Leadership
Whenever the cluster coordinator moves to a different node, every node publishes a LeaderChanged event on its local event stream, alongside NodeJoined and NodeLeft, carrying the new leader’s address. Like the other topology events, it exists for observability (monitoring, logging, dashboards); leadership itself is managed entirely by the framework.
Leadership
LeaderChanged is eventually consistent. It is derived from cluster topology events, so it is published once per leadership change shortly after the cluster settles, and the node’s initial leadership is seeded silently rather than announced. Delivery is best-effort and may be missed under heavy event churn. Treat it as a notification, not a source of truth. When you need the authoritative current leader, call IsLeader(ctx) or Leader(ctx).

See also