Skip to main content
This guide covers all breaking changes and migration steps when moving from GoAkt v3.x to v4.0.0.

Executive Summary

v4.0.0 delivers simplification and performance:
ThemeKey Changes
Unified APIsSingle actor reference (*PID), single lookup (ActorOf), unified scheduler
Type flexibilityany replaces proto.Message; CBOR supports arbitrary Go types
RemotingConfig-only public API; client is internal
IdentityPath interface replaces *address.Address
PerformanceLow-GC serialization, lock-free type registry, single-allocation frames

Quick Reference Table

FromTo
proto.Message in handlers/call sitesany
goaktpb.* typesactor.* (e.g. actor.PostStart, actor.PoisonPill)
ActorRef*PID
ActorOf β†’ (addr, pid, err)ActorOf β†’ (*PID, error)
Actors() / ActorRefs(ctx, timeout)Actors(ctx, timeout) ([]*PID, error)
LocalActor(name)ActorOf(ctx, name)
RemoteActor(ctx, name)ActorOf(ctx, name)
RemoteSchedule* methodsSchedule* with remote PID from ActorOf
GetPartition(name)Partition(name)
pid.Address()pid.Path()
ctx.SenderAddress()ctx.Sender().Path()
ctx.ReceiverAddress()ctx.Self().Path()
remote.Remoting / remote.ClientActor system APIs; configure via WithRemote(config)
WithRemotingWithRemote(config)
address packageinternal/address; use Path interface from pid.Path()
testkit.Probe.SenderAddress()Sender() then pid.Path() or pid.IsRemote()

Breaking Changes

1. Message types: proto.Message β†’ any

All message-passing APIs now accept any instead of proto.Message:
SurfaceOldNew
PID.Tell / PID.Askmessage proto.Messagemessage any
PipeTo / PipeToNamefunc() (proto.Message, error)func() (any, error)
Schedule / ScheduleOnce / ScheduleWithCronmessage proto.Messagemessage any
AskGrain / TellGrainmessage proto.Messagemessage any
Migration: Replace proto.Message in Receive handlers and call sites with any. Serialization is handled by the remote.Serializer layer.

2. System messages: goaktpb β†’ actor

The goaktpb package has been removed. System message types are plain Go structs in actor:
Old (goaktpb)New (actor)
goaktpb.PostStartactor.PostStart
goaktpb.PoisonPillactor.PoisonPill
goaktpb.Terminatedactor.Terminated
goaktpb.Deadletteractor.Deadletter
goaktpb.NoMessageactor.NoMessage
goaktpb.ActorStartedactor.ActorStarted
goaktpb.ActorStoppedactor.ActorStopped
goaktpb.ActorPassivatedactor.ActorPassivated
goaktpb.ActorChildCreatedactor.ActorChildCreated
goaktpb.ActorRestartedactor.ActorRestarted
goaktpb.ActorSuspendedactor.ActorSuspended
goaktpb.ActorReinstatedactor.ActorReinstated
goaktpb.NodeJoinedactor.NodeJoined
goaktpb.NodeLeftactor.NodeLeft
Use constructors (e.g. actor.NewDeadletter(...)) instead of protobuf struct literals. Timestamps are time.Time instead of *timestamppb.Timestamp.

3. Actor reference: ActorRef β†’ *PID

ActorRef has been removed. *PID is the sole actor reference for both local and remote actors. Use pid.IsLocal() or pid.IsRemote() when location matters. Migration: Replace ActorRef with *PID; methods returning ActorRef slices now return []*PID.

4. Lookup: unified ActorOf

LocalActor, RemoteActor, and ActorRefs are replaced by a single ActorOf(ctx, name):
CaseOldNew
Actor found locally(addr, pid, nil)(pid, nil) β€” local PID
Actor found in cluster(addr, nil, nil)(pid, nil) β€” remote PID
Not found(nil, nil, err)(nil, err)
Migration: Use pid.Path() for host/port/name/system; pid.IsLocal() / pid.IsRemote() for location.

5. Actors merged with ActorRefs

// Before
Actors() []*PID
ActorRefs(ctx, timeout) ([]ActorRef, error)

// After
Actors(ctx context.Context, timeout time.Duration) ([]*PID, error)
timeout bounds cluster scan and is ignored when not in cluster mode.

6. Remote scheduler methods removed

RemoteScheduleOnce, RemoteSchedule, RemoteScheduleWithCron are removed. Migration: Obtain remote PID via ActorOf and use unified ScheduleOnce, Schedule, or ScheduleWithCron.

7. GetPartition renamed to Partition

Migration: sys.GetPartition(name) β†’ sys.Partition(name).

8. Address β†’ Path

pid.Address() is replaced by pid.Path(), which returns a Path interface. The address package is now internal.
OldNew
pid.Address().String()pid.Path().String()
pid.Address().Name()pid.Path().Name()
pid.Address().Host()pid.Path().Host()
pid.Address().Port()pid.Path().Port()
pid.Address().HostPort()pid.Path().HostPort()
pid.Address().System()pid.Path().System()
pid.Address().Equals(other)pid.Path().Equals(otherPath)
Path() returns nil for nil PID. Guard accordingly (e.g. for NoSender).

9. Remoting configuration

The remoting client is internal. The remote package exposes only configuration and protocol types (Config, Serializer, Compression, ContextPropagator, etc.). Migration: Replace remote.Remoting / remote.Client β€” use actor system and client.Node APIs. Replace remote.NewRemoting() / remote.NewClient() β€” use WithRemote(config) when creating the actor system. For tests: mockremote.NewClient(t) for mock injection.

10. Log package

Custom Logger implementations must implement additional methods:
MethodPurpose
InfoContext, InfofContext, WarnContext, WarnfContext, ErrorContext, ErrorfContext, DebugContext, DebugfContextContext-aware logging for trace propagation
LogLevel() LevelReturns configured minimum severity level
Enabled(level Level) boolCheck before expensive work
With(keyValues ...any) LoggerStructured fields; keys and values alternate
LogOutput() []io.WriterReturns configured output destinations
Flush() errorDrain buffered output
StdLogger() *log.LoggerCompatibility with stdlib *log.Logger
Migration: Use log.DiscardLogger in tests if you need a no-op.

11. Testkit & ReceiveContext

RemovedReplacement
testkit.Probe.SenderAddress()Sender() then pid.Path() or pid.IsRemote()
ctx.SenderAddress()ctx.Sender().Path() (guard for nil sender)
ctx.ReceiverAddress()ctx.Self().Path()

New Additions

Pluggable serialization

  • ProtoSerializer β€” Default for proto.Message. No change for protobuf users.
  • CBORSerializer β€” For arbitrary Go types. Register via remote.WithSerializers(new(MyMessage), remote.NewCBORSerializer()); types are auto-registered.
  • Custom β€” Implement remote.Serializer; register via WithSerializers on remote.Config.

Path interface

pid.Path() returns a Path interfaceβ€”location-transparent actor identity. See Location Transparency.

New sentinel errors

  • errors.ErrRemotingDisabled β€” Remote operation attempted but remoting not configured
  • errors.ErrNotLocal β€” Operation requires local PID but remote PID provided

PID.Kind()

pid.Kind() returns the reflected type name of the actor backing the PID. Empty string for remote PIDs.

Log implementations

  • log.NewSlog(level, writers...) β€” stdlib slog with low-GC optimizations
  • log.NewZap(level, writers...) β€” Zap with buffered file output
  • log.DiscardLogger β€” No-op for tests

Remoting Capabilities

Remoting is configured via remote.NewConfig(bindAddr, bindPort, opts...) and passed to WithRemote(config).

Transport

  • Length-prefixed TCP frames; pooled connections
  • Optional TLS
  • Compression: NoCompression, GzipCompression, ZstdCompression (default), BrotliCompression

Server options

OptionPurpose
WithWriteTimeoutConnection close if no data written
WithReadIdleTimeoutHealth check / ping
WithMaxFrameSize16KB–16MB
WithCompressionMust match client
WithContextPropagatorTrace IDs, auth tokens across boundaries
WithSerializersPer-type or per-interface serializers

Remote spawn options

When spawning on a remote node via WithHostAndPort(host, port):
OptionPurpose
WithHostAndPort(host, port)Target remote node
WithRelocationDisabled()No relocation on host failure
WithDependencies(...)Injected on remote node
WithStashing()Enable stashing
WithPassivationStrategy(strategy)Passivation behavior
WithReentrancy(reentrancy)Async request policy
WithSupervisor(supervisor)Failure handling
WithRole(role)Require remote node role

Migration Checklist

  1. Update import path: goakt/v3 β†’ goakt/v4
  2. Replace proto.Message with any in handlers and call sites
  3. Replace goaktpb.* with actor.*; use constructors for structs
  4. Replace ActorRef with *PID
  5. Replace LocalActor / RemoteActor with ActorOf(ctx, name)
  6. Replace pid.Address() with pid.Path()
  7. Replace ctx.SenderAddress() / ctx.ReceiverAddress() with ctx.Sender().Path() / ctx.Self().Path()
  8. Replace GetPartition with Partition
  9. Replace WithRemoting with WithRemote(config)
  10. Remove address package imports; use Path interface
  11. Update custom Logger implementations with new methods
  12. Update tests: Probe.SenderAddress() β†’ Sender() + Path()
  13. Remove RemoteSchedule* usage; use Schedule* with ActorOf PID