Releases: Tochemey/goakt
v4.2.0
What's Changed
✨ Features
- log: Add
NewSlogFromto create aLoggerfrom an existing*slog.Logger(#1133) - Add CRDT capabilities (#1135). More info can be found in the doc
- Add data center–aware CRDT features (#1139)
🐛 Fixes
- Fix context propagation for grain and broken tests (#1137)
🔧 Refactoring
- Refactor distributed data (ddata) implementation (#1138)
Full Changelog: v4.1.1...v4.2.0 and https://github.com/Tochemey/goakt/blob/main/CHANGELOG.md#v420---2026-03-31
v4.1.1
✨ New Additions
🌊 stream Package — Reactive Streams for GoAkt
A new top-level stream package brings demand-driven, actor-native stream processing to GoAkt.
Every pipeline stage runs inside a GoAkt actor, inheriting supervision, lifecycle management, and
location transparency automatically. Pipelines are lazy: nothing executes until RunnableGraph.Run
is called against a live ActorSystem.
🧩 Core Abstractions
| Abstraction | Description |
|---|---|
Source[T] |
Lazy description of a stream origin; assembled with Via / To into a RunnableGraph |
Flow[In, Out] |
Lazy description of a transformation stage; type-safe, composable |
Sink[T] |
Lazy description of a terminal consumer stage |
RunnableGraph |
Fully assembled pipeline; a value type that can be Run multiple times for independent instances |
StreamHandle |
Live handle returned by Run; exposes ID(), Done(), Err(), Stop(ctx), Abort(), and Metrics() |
📥 Sources
| Constructor | Description |
|---|---|
Of[T](values...) |
📋 Finite source from a fixed set of values |
Range(start, end) |
🔢 Integer range source ([start, end)) |
FromChannel[T](ch) |
📡 Reads from a Go channel; completes when the channel closes |
FromActor[T](pid) |
🤖 Pulls from a GoAkt actor using the PullRequest / PullResponse[T] protocol |
Tick(interval) |
⏱️ Emits time.Time on a fixed interval; runs until cancelled |
Merge[T](sources...) |
🔀 Fans N sources into one; completes when all inputs complete |
Combine[T,U,V](left, right, fn) |
🤝 Zips two sources pairwise via fn; zip semantics |
Broadcast[T](src, n) |
📢 Fans one source out to N independent branches |
Balance[T](src, n) |
⚖️ Distributes one source across N branches (round-robin with backpressure) |
FromConn(conn, bufSize) |
🌐 Reads []byte frames from a net.Conn |
Unfold[S,T](seed, step) |
🌱 Generates values from a seed with a stateful step function |
🔄 Flows
| Constructor | Description |
|---|---|
Map[In,Out](fn) |
🗺️ Type-changing transformation; no error path |
TryMap[In,Out](fn) |
🛡️ Transformation with error; ErrorStrategy controls failure handling |
Filter[T](predicate) |
🔍 Keeps only elements where predicate returns true |
FlatMap[In,Out](fn) |
📤 Expands each element into a slice of outputs |
Flatten[T]() |
📦 Unwraps []T elements into individual elements |
Batch[T](n, maxWait) |
📦 Groups elements into []T slices of at most n; flushes early after maxWait |
Buffer[T](size, strategy) |
💾 Asynchronous buffer with configurable overflow strategy |
Throttle[T](n, per) |
🚦 Limits throughput to at most n elements per per duration |
Deduplicate[T]() |
🔁 Suppresses consecutive duplicate elements (T must be comparable) |
Scan[In,State](zero, fn) |
📊 Running accumulation; emits each intermediate state |
WithContext[T](key, value) |
🏷️ Labels a tracing boundary; passes elements through unchanged |
ParallelMap[In,Out](n, fn) |
⚡ Applies fn concurrently with up to n goroutines; unordered output |
OrderedParallelMap[In,Out](n, fn) |
📐 Like ParallelMap but preserves input order (min-heap resequencing) |
📤 Sinks
| Constructor | Description |
|---|---|
ForEach[T](fn) |
🔄 Calls fn for each element |
Collect[T]() |
📋 Accumulates all elements; retrieve via Collector[T].Items() after completion |
Fold[T,U](zero, fn) |
➕ Reduces to a single value; retrieve via FoldResult[U].Value() |
First[T]() |
🥇 Captures the first element then cancels upstream |
Ignore[T]() |
🗑️ Discards all elements; useful for side-effecting flows |
Chan[T](ch) |
📡 Writes each element to a Go channel; applies natural backpressure when full |
ToActor[T](pid) |
🤖 Forwards each element to a GoAkt actor via Tell |
ToActorNamed[T](system, name) |
🏷️ Resolves actor by name on each element and forwards via Tell |
🏗️ Pipeline DSL
From[T](src)— starts aLinearGraph[T]fluent builderLinearGraph[T].Via(flow)— chains a type-preserving flowLinearGraph[T].To(sink)— attaches a sink and returns aRunnableGraphViaLinear[In,Out](g, flow)— type-changing step on aLinearGraphVia[In,Out](src, flow)— package-level free function for type-changing flows onSourceGraphDSL — named-node builder for non-linear topologies (fan-out, fan-in, merge)
🔙 Backpressure & Configuration
- Credit-based demand propagation: sinks signal demand upstream; sources produce only what is requested
StageConfig— per-stage knobs:InitialDemand,RefillThreshold,ErrorStrategy,RetryConfig,OverflowStrategy,BufferSize,Mailbox,Name,Tags,Tracer,Fusion,MicroBatch,PullTimeout,OnDropErrorStrategy—FailFast(default),Resume(skip),Retry(withRetryConfig.MaxAttempts),SuperviseOverflowStrategy—DropTail,DropHead,DropBuffer,Backpressure,FailFusionMode—FuseStateless(default, fuses adjacentMap/Filterstages into one actor),FuseNone,FuseAggressive
🔭 Observability
Tracerinterface — per-element hooks:OnElement,OnDemand,OnError,OnComplete; attach viaWithTracerMetricsReporterinterface — snapshot forwarding to external systems (Prometheus, OTel)StreamHandle.Metrics()— liveStreamMetricssnapshot with element-in, element-out, and error counts
⚡ Performance Internals
- 🕐
time.Now()guarded bytracer != nilon the element hot path — no clock overhead when tracing is disabled - 📦
fusedFlowActor(stage-fusion fast path) uses credit-based batch demand refill instead of onestreamRequestallocation per element, reducing demand-message overhead by ~160x under default configuration - 📡
chanSourceActorbridges external channels to the actor mailbox in batches of up to 64 values peractor.Tellcall (drain-without-blocking strategy), significantly reducing mailbox-enqueue overhead on high-throughput channels - 🏊
connSourceActorpools full-sized read buffers viasync.Pool; each element sent downstream is an exact-sized copy, eliminating a per-read heap allocation - 🗑️
FilterandDeduplicatereturnnilinstead of an empty[]any{}for skipped elements, removing a slice allocation on every filtered-out element - 🔢 Stream sub-IDs use an atomic counter instead of UUID generation, avoiding an entropy read and string-formatting cost on every
RunnableGraph.Runcall - 🐛
flowActor.tryFlushOutputnow firesTracer.OnElementwith the definitively-assigned sequence number, fixing incorrect seqNo reporting for multi-output transforms (FlatMap)
🔀 Pull Request
- chore(deps): update module golang.org/x/net to v0.52.0 by @renovate[bot] in #1124
- chore(deps): update actions/upload-artifact action to v7 by @renovate[bot] in #1125
- feat: add streaming capabilities by @Tochemey in #1126
- chore(deps): update dependency go to v1.26.1 by @renovate[bot] in #1127
- chore(deps): update golang docker tag to v1.26.1 by @renovate[bot] in #1128
- chore(deps): update module github.com/bufbuild/buf to v1.66.1 by @renovate[bot] in #1129
- chore(deps): update dependency golangci/golangci-lint to v2.11.3 by @renovate[bot] in #1130
Full Changelog: v4.1.0...v4.1.1 and https://github.com/Tochemey/goakt/blob/main/changelog.md#v411---2026-03-27
v4.1.0
What Changed
⚠️ Breaking Changes
This release refines the actor identity model introduced in v4.0.0 by propagating the Path interface through all lifecycle and dead-letter event types, replacing raw string addresses throughout the event API.
Lifecycle & Dead-Letter Events Now Use Path
The following types have been updated:
| Type | Old | New |
|---|---|---|
Deadletter |
Sender() string, Receiver() string |
Sender() Path, Receiver() Path |
Terminated |
Address() string |
ActorPath() Path |
ActorStarted |
Address() string |
ActorPath() Path |
ActorStopped |
Address() string |
ActorPath() Path |
ActorPassivated |
Address() string |
ActorPath() Path |
ActorChildCreated |
Address() string, Parent() string |
ActorPath() Path, Parent() Path |
ActorRestarted |
Address() string |
ActorPath() Path |
ActorSuspended |
Address() string |
ActorPath() Path |
ActorReinstated |
Address() string |
ActorPath() Path |
Migration Guide
- Replace
.Address()calls on event types with.ActorPath()
// Before
handler := func(evt *actor.ActorStarted) {
fmt.Println(evt.Address()) // string
}
// After
handler := func(evt *actor.ActorStarted) {
fmt.Println(evt.ActorPath().String()) // Path
}This is a focused follow-up to v4.0.0's
Pathinterface introduction, completing the migration of event types to the unified identity model. No new features or bug fixes are included in this release.
Pull Requests
- chore: code maintenance by @Tochemey in #1117
- chore(deps): update go minor and patch by @renovate[bot] in #1119
- chore(deps): update module github.com/bufbuild/buf to v1.66.0 by @renovate[bot] in #1120
- chore: upgrade dependencies by @Tochemey in #1121
- chore(deps): update dependency golangci/golangci-lint to v2.10.1 by @renovate[bot] in #1118
- refactor: 💥 use Path type in dead letters and terminated messages by @Tochemey in #1122
Full Changelog: v4.0.0...v4.1.0
v4.0.0
GoAkt v4.0.0
Production-ready release — Unified APIs, typed messages with any, pluggable serializers, and config-only remoting.
Installation
go get github.com/tochemey/goakt/v4What's New
v4.0.0 delivers simplification and performance across the board:
*PID— Sole actor reference for both local and remote actors;ActorRefremovedActorOf(ctx, name)— Unified lookup replacingLocalActorandRemoteActor; returns a local or remote PIDActors(ctx, timeout)— Replaces bothActors()andActorRefs(); returns([]*PID, error)anymessages —proto.Messageconstraint removed from all public APIs (Tell,Ask,Schedule*,AskGrain,TellGrain)- Pluggable serializers — ProtoSerializer (default) for protobuf; CBORSerializer for arbitrary Go types with automatic type registration
Pathinterface — Location-transparent actor identity viapid.Path();addresspackage moved tointernal/address- Config-only remoting — Remoting client is internal; configure via
WithRemote(config)on the actor system - Consistent Hash Router — New routing strategy for partition-affinity and sticky-session use cases
- Extended
Logger— Context-aware and introspection methods added; slog and Zap implementations included
Breaking Changes
Key migrations at a glance:
| From | To |
|---|---|
proto.Message in handlers / call sites |
any |
ActorRef |
*PID |
ActorOf → (addr, pid, err) |
ActorOf → (*PID, error) |
LocalActor(name) / RemoteActor(ctx, name) |
ActorOf(ctx, name) |
Actors() + ActorRefs(ctx, timeout) |
Actors(ctx, timeout) ([]*PID, error) |
RemoteScheduleOnce / RemoteSchedule / RemoteScheduleWithCron |
Schedule* with remote PID from ActorOf |
pid.Address() |
pid.Path() |
ctx.SenderAddress() / ctx.ReceiverAddress() |
ctx.Sender().Path() / ctx.Self().Path() |
testkit.Probe.SenderAddress() |
Sender() + pid.Path() |
goaktpb.* |
actor.* (e.g. actor.PostStart, actor.PoisonPill) |
WithRemoting |
WithRemote(config) (actor system) / WithRemoteConfig(config) (client node) |
Custom Logger implementations |
Must implement new context-aware and introspection methods |
Full migration guide: CHANGELOG_V400.md
Bug Fixes
- Fixed stale kind entries causing
ErrKindAlreadyExists/ErrSingletonAlreadyExistson new leader election.cleanupClusternow checkspid.singletonSpec != nilinstead ofpid.IsSingleton(), which was incorrectly returningfalseafterpid.reset()cleared the singleton state during shutdown.
Resources
- Documentation: docs.goakt.dev
- Migration & changelog: CHANGELOG_V400.md
- Examples: goakt-examples
- v3.x continues to receive bug fixes on
release/3.14
Pull Requests
- docs: add architecture documentation by @Tochemey in #1101
- feat: replace proto.Message with any and allow custom remote serialization by @Tochemey in #1102
- refactor: remove unnecessary methods on receive context by @Tochemey in #1103
- refactor: move remote client to internal and refactor spawn by @Tochemey in #1105
- refactor: enhance the PID implementation and remote calls by @Tochemey in #1106
- feat: ✨ v400: additional refactoring and api changes by @Tochemey in #1107
- feat(v400): ✨ add the selfmanaged discovery provider by @Tochemey in #1108
- docs(v400): add an logo and icon images by @Tochemey in #1109
- fix(v400): fix ask and tell using the api to handle remote PID by @Tochemey in #1110
- feat(v400): set the default cbor serializer by @Tochemey in #1111
- perf: add proactive cluster sync on node join by @Tochemey in #1113
- chore(deps): update dependency golangci/golangci-lint to v2.10.1 by @renovate[bot] in #1112
- refactor: handle context propagation omitted remote handlers by @Tochemey in #1114
- feat: add consistent hash router by @Tochemey in #1115
Full Changelog: v3.14.0...v4.0.0 and https://github.com/Tochemey/goakt/blob/main/changelog.md#v400---2026-03-05
v3.14.0
🚀 Oveview
This release brings multi-datacenter support, a high-performance protobuf-over-TCP remoting stack, and important fixes for channel pooling, timeouts, and data races. It also adds an option to disable grain relocation and several allocation and lock optimizations across the hot paths.
✨ What's New
🌐 Multi-datacenter support
- DC-transparent messaging — actors and grains can communicate across datacenters without changing your code.
- Pluggable control plane — use NATS JetStream or Etcd for coordination.
- DC-aware placement — spawn actors in a specific datacenter with
SpawnOnand theWithDataCenteroption. - See the
datacenterpackage andWithDataCenterfor details.
🛡️ Grain relocation control
WithGrainDisableRelocation— disable actor/grain relocation when you don’t need it (e.g. stateless actors or short-lived grains).
🐛 Bug Fixes
| Area | Fix |
|---|---|
Shutdown |
preShutdown no longer builds or persists peer state when relocation is disabled (WithoutRelocation), so shutdown proceeds correctly and avoids unnecessary cluster work. |
Channel pool |
Fixed channel-pool poisoning in GrainContext.NoErr(): sending on both response and error channels for sync calls could corrupt the pool under scheduling races. |
Timeouts |
Fixed localSend timeout/cancel paths returning channels to the pool while the grain goroutine could still write, preventing stale values in later requests. |
Data race |
Fixed race in PID.recordProcessedMessage() by replacing the runtime type assertion on passivationStrategy with an atomic.Bool set at init. |
⚡ Performance & Remoting
🌐 New remoting stack (protobuf over TCP)
ConnectRPC/HTTP-based remoting is replaced with a protobuf-over-TCP server and connection-pooling client:
- Multi-loop TCP accept with
SO_REUSEPORT,TCP_FASTOPEN, andTCP_DEFER_ACCEPTfor lower connection-setup latency and kernel-level load balancing. - Sharded
WorkerPoolfor connection dispatch, reducing contention under high concurrency. - Length-prefixed wire protocol with dynamic protobuf type dispatch via the global registry (no per-service stubs or HTTP path routing).
FramePoolwith power-of-two bucketedsync.Pool(256 B – 4 MiB) for read buffers and frame slices to cut heap allocations and GC pressure.- LIFO connection pool in the TCP client with lazy stale-connection eviction and no background goroutines.
- Pluggable
ConnWrappercompression (Zstandard, Brotli, Gzip) on both client and server.
♻️ Pooling & allocation
- Bounded channel-based pools instead of
sync.PoolforReceiveContext,GrainContext, and response/error channels on theTell,Ask,SendAsync, andSendSynchot paths to avoid cross-P thrashing and madvise overhead. PID.latestReceiveTimeNanoswitched fromatomic.Timetoatomic.Int64(Unix nanoseconds) to remove ~24-byte interface allocations per message.- Cached
Address.String()andGrainIdentity.String()to avoid repeatedfmt.Sprintfon every message. - Mailbox nodes in
UnboundedMailboxandgrainMailboxuse plain pointers instead ofatomic.Pointeron enqueue/dequeue. - Inlined
deferclosures in Ask-path functions (PID.Ask,actor.Ask,handleRemoteAsk, grainlocalSend) to remove per-call closure allocations.
🔓 Locking & contention
ActorOfdoes local actor lookup before taking the system-wideRWMutex;SendAsync/SendSyncbypass thePID.ActorSystem()getter lock, reducing read-lock acquisitions from three to one on the local path.passivationManager.Touchcoalesced via an atomic timestamp guard (lastPassivationTouch), so mutex contention drops from once-per-message to at most once per 100 ms.
📦 Upgrade notes
- Remoting now uses the new TCP-based stack by default. No API changes are required for typical usage.
- Use
WithGrainDisableRelocationwhen you want to skip relocation for certain grains. - For multi-DC setups, use the
datacenterpackage andWithDataCenterwithSpawnOn.
🔗 Pull requests
Features & refactors
- ✨ Multi-datacenter capabilities — @Tochemey in #1085
- 🔄 Datacenter implementation refactor — @Tochemey in #1092
- ⚡ Performance optimisations — @Tochemey in #1094
- 📚 Comprehensive documentation — @Tochemey in #1096
Dependencies & tooling
- chore(deps):
github.com/zeebo/xxh3→ v1.1.0 — @renovate[bot] in #1088 - chore(deps):
actions/checkout→ v6.0.2 — @renovate[bot] in #1086 - chore(deps):
golangci/golangci-lint→ v2.8.0 — @renovate[bot] in #1087 - chore(deps):
github.com/redis/go-redis/v9→ v9.17.3 — @renovate[bot] in #1089 - chore(deps):
github.com/bufbuild/buf→ v1.64.0 — @renovate[bot] in #1091 - chore(deps): Go → v1.25.6 — @renovate[bot] in #1090
- chore(deps):
github.com/klauspost/compress→ v1.18.4 — @renovate[bot] in #1093 - chore(deps):
github.com/bufbuild/buf→ v1.65.0 — @renovate[bot] in #1098
Full Changelog: v3.13.0...v3.14.0
v3.13.0
🚀 What's Changed
✨ Features
- feat: added utility methods by @Tochemey in #1060
- feat(actor): ✨ reject user messages during system shutdown by @Tochemey in #1073
🐛 Bug Fixes
⚡ Performance Improvements
- perf(actor): refactor how node publishes its state before gracefully leaving by @Tochemey in #1079
- perf: ⚡ refactor default logger to enhance performance by @Tochemey in #1080
- perf: ⚡ refactor remoting to boost performance by @Tochemey in #1083
🔨 Refactoring
🧹 Maintenance & Dependency Updates
- chore(deps): update Golang Docker tag to
v1.25.5by @renovate[bot] in #1059 - chore(deps): update module
github.com/bufbuild/buftov1.62.1by @renovate[bot] in #1075 - chore(deps): update module
github.com/bufbuild/buftov1.63.0by @renovate[bot] in #1081
Full Changelog:
v3.12.1
What's Changed
✨ New Features
- actor: Added reentrancy capabilities by @Tochemey in #1050
- grain: Added bounded mailbox support for grains by @Tochemey in #1055. This feature was engineered by @andreas-lindfalk
🛠 Refactors & Maintenance
- actor: Refactored the reentrancy implementation by @Tochemey in #1052
- core: General code maintenance and cleanup by @Tochemey in #1053
📚 Documentation
- Updated copyright headers by @Tochemey in #1049
- Documented reentrancy in the feature list by @Tochemey in #1051
Full Changelog: v3.12.0...v3.12.1 and https://github.com/Tochemey/goakt/blob/main/changelog.md#v3121---2026-06-01
v3.12.0
🚀 Highlights
This release focuses on robustness, resilience, and developer experience, with significant improvements across clustering, grains, scheduling, and maintenance.
✨ New Features
- ✨ Remoting calls for Grains Enables remote interactions with Grains for distributed workflows. (@Tochemey · #1027)
- 🧪 Basic TestKit support for Grains Introduces foundational testing utilities to simplify Grain testing. (@Tochemey · #1033)
- 🧭 Default supervisor configuration
SpawnOnnow uses the system-wide default supervisor strategy. A new option,WithDefaultSupervisor, has been added to explicitly configure it. (@Tochemey · #1018)
🐛 Bug Fixes
- Grain activation revamp Prevents panics and duplicate activations during Grain lifecycle handling. (@Tochemey · #1023)
- Recovery support for Grain de/activation Adds recovery capabilities to improve fault tolerance. (@Tochemey · #1030)
- Actor count correctness Fixes mismatches as well as underflow and overflow issues in actor counts. (@Tochemey · #1035)
- Scheduler reliability Fixes an issue where
ScheduleOnce()did not always trigger as expected. (@majiayu000 · #1039)
♻️ Refactors & Improvements
- Address implementation overhaul Revamps address handling and adds support for configuring a default supervisor. (@Tochemey · #1019)
- Supervisor system overhaul (@Tochemey · #1018)
- Supervisor logic has been moved into its own dedicated package:
supervisor - Migrating existing code only requires replacing
actorwithsupervisorBreaking Change
- Supervisor logic has been moved into its own dedicated package:
- Improved relocation semantics (@Tochemey · #1018)
- Ensures child actors are not relocated unintentionally during relocation
- Adds support to relocate actors with their configured supervisor strategy
- Restart behaviour revamp The
Restartimplementation now restarts the entire child family tree, ensuring consistent recovery semantics. (@Tochemey · #1018) - Cluster storage DB file creation Reimplements DB file creation logic for improved reliability. (@Tochemey · #1025)
- Cluster singleton resilience Makes singleton spawning more robust in clustered environments. (@Tochemey · #1038)
- Reduced shutdown log noise Avoids unnecessary log spam during actor system shutdown. (@Tochemey · #1032)
- General code maintenance Internal refactors and cleanup for long-term maintainability. (@Tochemey · #1026)
🧹 Chores & Dependency Updates
- Cluster engine dependency upgrade Keeps the cluster engine up to date. (@Tochemey · #1041)
- Dependency maintenance Routine dependency updates and housekeeping. (@Tochemey · #1042)
🙌 New Contributors
- @majiayu000 made their first contribution in #1039
- @TrueBrain made significant contribution in enhancing the cluster engine in #1036
Full Changelog: https://github.com/Tochemey/goakt/blob/main/changelog.md
v3.11.2
🧰 Maintenance
- refactor: code maintenance by @Tochemey in #1006
- chore: maintenance by @Tochemey in #1008
- chore: upgrade dependencies by @Tochemey in #1010
- chore(deps): update actions/checkout action by @renovate[bot] in #1012
- chore(deps): update golangci/golangci-lint-action action to v9 by @renovate[bot] in #1013
- chore(deps): ⬆️ dependencies upgrade by @Tochemey in #1015
Full Changelog: v3.11.1...v3.11.2
v3.11.1
🚀 Highlights
- ⚡ Refactor the implementation of
Killmethod to take into consideration cluster mode.
🔗 Pull Requests
- chore(deps): update actions/checkout action to v6 by @renovate[bot] in #999
- docs: enhance readme by @Tochemey in #1001
- refactor: refactor the implementation of kill by @Tochemey in #1004
Full Changelog: v3.11.0...v3.11.1