Migration from Apache Pekko 1.x to 2.x
Apache Pekko 2.x is not Binary Compatible with Apache Pekko 1.x.
The major difference is that some deprecated code has been removed but there are also a small number of breaking changes where deprecation was not feasible.
It is possible that some simple code compiled with Pekko 1.x libs will work with Pekko 2.x but you should not rely on this.
Pekko 1.x is still maintained and major bugs will be fixed in it.
Start by upgrading to latest Pekko 1.x releases
Some of the changes in Pekko 2.x have been backported although these changes are normally only done when they fix important bugs.
Some additional code has been deprecated in the most recent 1.x releases and the compile warnings will help you with moving onto better supported APIs.
Change any code that relies on deprecated APIs in Pekko 1.x
Not every deprecated API has been removed in Pekko 2.x but many have been.
Java API users may find that they have more deprecations to deal with because the Scala API is more stable and there were a few mistakes in the Java API where Scala classes leaked into some of the Java API methods.
Deprecations in Pekko 2.x
- The
Source.queueoverloads that take anOverflowStrategyand materialize aSourceQueueWithCompleteare deprecated. Their asynchronousoffercan hang indefinitely underOverflowStrategy.backpressurewhen downstream stalls. UseSource.queue(bufferSize)(which materializes aBoundedSourceQueuewith synchronous feedback),Source.actorRefWithBackpressureorMergeHub.sourceinstead. See Migrating from the deprecated Source.queue overloads for a replacement perOverflowStrategy, with code examples.
Additional Breaking Changes in Pekko 2.x
Java API
pekko.japi.Functionand the related classes (includingpekko.japi.function.FI,JAPIandpekko.japi.Option) have been removed in favour ofpekko.japi.function.Functionand the otherpekko.japi.functiontypes, which the Java DSLs now use throughout. Lambdas should recompile unchanged, but if you declared variables, fields or methods with the removed types, change your imports. (PR2007, PR2143)- A number of Java DSL methods that exposed Scala types now use Java types instead:
CompletionStageinstead of ScalaFuture,Optionalinstead ofOption,pekko.japi.Pairinstead ofTuple2and Java collections instead of Scala collections. See the release notes for the list of changes. FSMTransitionHandlerBuilder.buildandFSMTransitionHandlerBuilder.statenow usepekko.japi.Pairinstead ofscala.Tuple2. Java users need to update their code to usePairinstead ofTuple2. (PR3378)- The Java DSL
SourceWithContextgraph shape now usespekko.japi.Pairinstead ofscala.Tuple2. Java code that passes aSourceWithContextdirectly toGraphDSLmust usePair-typed stages. Code that converts it withasSource()already usesPairand requires no changes. (PR3388)
Actors
ReceiveTimeoutchanged from acase objectsingleton to afinal case classthat carries the configured timeout duration. Scala pattern matches must use a type pattern (case timeout: ReceiveTimeout =>) instead of a stable identifier pattern, and theReceiveTimeout.getInstance()method has been removed from the Java API; Java users should match onReceiveTimeout.classinstead. (PR3399)
Streams
- In the Scala DSL for Flow and Source, the
watchTerminationfunction call no longer needs an empty param list before a second param list. Instead ofwatchTermination(){ ... }, you now must usewatchTermination{ ... }. (PR2378)
TLS
The deprecated PekkoSSLConfig convenience API and the stream methods accepting it were removed, along with the ssl-config dependency. (PR2127)
To migrate, initialize an SSLContext with the required key and trust managers, and supply a factory that creates a new javax.net.ssl.SSLEngine for each connection:
- Set the engine’s client or server mode, and apply the cipher suites, protocols and client-authentication settings previously passed in
NegotiateNewSessiondirectly to the engine or itsSSLParameters. - Client factories must preserve peer identity checks: use
sslContext.createSSLEngine(host, port), callsetUseClientMode(true), and enable hostname verification withsetEndpointIdentificationAlgorithm("HTTPS")on the engine’sSSLParameters. - For DNS names, also configure SNI with
SSLParameters.setServerNamescontaining anSNIHostNamefor that hostname. - Apply those parameters with
SSLEngine.setSSLParametersbefore returning the engine.
The factory is accepted by TLS.applyTLS.create and the current TcpTcp TLS methods. Replace outgoingTlsConnection with outgoingConnectionWithTls and bindTls with bindWithTls. Also replace bindAndHandleTls with bindAndHandleWithTls. See the Scala and Java TLS examples for general engine setup, adding the client hostname-verification and SNI settings described above.
Cancelling a TLS operator’s transport output now reports SessionTruncated on the plaintext output when a session was established and the peer’s close_notify has not been processed, instead of completing silently. If you use the TLS BidiFlow directly, handle SessionTruncated as well as SessionBytes, and cancel the plaintext output too if it will no longer be consumed. Tcp.bindWithTls and Tcp.outgoingConnectionWithTls filter out this signal. See Stream IO for details. (PR3740)
Remoting
- In pekko-remote Artery comms,
"PEKK"is now the default magic header (pekko.remote.artery.advanced.tcp-magic). See Changing TCP magic header for what this means for rolling updates. (PR3425)
Cluster Sharding
RemoveInternalClusterShardingDatano longer supports removing Cluster Sharding coordinator data stored by Akka 2.3.x. The-2.3program argument and theremove2dot3Dataparameter ofRemoveInternalClusterShardingData.removehave been removed. (PR3608)ShardCoordinator.AbstractShardAllocationStrategy, the Java API for custom shard allocation strategies, no longer leaks Scala types. ItsallocateShardandrebalancemethods now return ajava.util.concurrent.CompletionStageinstead of a ScalaFuture, and the current shard allocations are passed as ajava.util.Map<ActorRef, ? extends List<String>>instead of ajava.util.Mapofscala.collection.immutable.IndexedSeq<String>. Update both method signatures and recompile Java subclasses against Pekko 2.x. Subclasses compiled against Pekko 1.x must not be used unchanged: they do not implement the new methods and will fail at runtime withAbstractMethodErrorwhen the coordinator invokes the strategy, preventing shard allocation or rebalancing. Scala implementations ofShardCoordinator.ShardAllocationStrategyare unaffected. Java strategies that need initialization on the coordinator node can additionally implementShardCoordinator.StartableAllocationStrategyand overridevoid start(); the coordinator calls it beforeallocateShardorrebalance. (PR3677)
Persistence
- In typed persistence (event sourcing and durable state, Scala and Java DSLs),
thenStophas moved fromReplyEffectto the newReplyEffectWithStopsubtype, whichreply,noReply,thenReplyandthenNoReplyreturn.stash().thenStop()no longer compiles, because it discarded the stashed command without replying. Code that callsthenStopon a value typed asReplyEffect, such as the result of a helper method, must declare that value or the helper’s return type asReplyEffectWithStopinstead. (PR3682) - In pekko-persistence-testkit, the
PersistenceTestKitReadJournaltag queries (eventsByTag,currentEventsByTag) and the liveeventsBySlicesquery now put aTimestampOffsetin their envelopes instead ofSequence. Code that cast these offsets toSequenceneeds updating; the per-persistence-id sequence number is still available asenvelope.sequenceNr. These queries also now accept aTimestampOffsetto resume from. (PR3615)
Dependency Changes in Pekko 2.x
- Java 17 is the minimum supported Java version and Scala 2.12 is no longer supported. Pekko 2.x is published for Scala 2.13 and Scala 3.3 (LTS), so Scala 3 users need Scala 3.3.x or later. Pekko 2.x is also tested with Scala 3.9.
- Agrona was updated from 1.x to 2.x, which means you may have to add the JVM option
--add-opens java.base/jdk.internal.misc=ALL-UNNAMEDif you use Pekko Remote with Artery. This applies to applications on the classpath as well as those that use the Java Module System (ALL-UNNAMEDcovers classpath code). - pekko-serialization-jackson depends on
at.yawk.lz4:lz4-javainstead of the unmaintainedorg.lz4:lz4-java. This change was made in Pekko 1.4.0, so you will only notice it if you are upgrading from an earlier Pekko 1.x release. The fork is a drop-in replacement with important bug fixes. If your build also depends onorg.lz4:lz4-java, consider excluding it so that the two jars are not both on the classpath. (PR2537) - The
ssl-configlibrary is no longer a dependency of Pekko and thessl-configconfiguration sections have been removed (see Removed configuration below). (PR2127) - Only one of
pekko-serialization-jackson(Jackson 2) andpekko-serialization-jackson3(Jackson 3) can be on the classpath. See Serialization bindings below.
Configuration Changes in Pekko 2.x
The reference.conf defaults have changed in a number of places. If you override any of the settings below in your application.conf, or rely on their Pekko 1.x defaults, review this list.
Some of these settings were also backported to Pekko 1.x releases. Those entries note the Pekko version in which the setting first appeared, so if you are already on that version or later you may have seen them before.
Changed default values
pekko.actor.default-dispatcher.fork-join-executor.minimum-runnablechanged from1to-1. The setting first appeared in Pekko 1.7.0, with a default of1. The value-1selects a JDK-aware default: on JDK 25 and later the effective value ismin(8, max(1, parallelism / 2)), and on earlier JDKs it is1, so the behavior only changes on JDK 25 and later. Set it explicitly to1to restore the Pekko 1.x behavior on all JDKs. The internal dispatcher (pekko.actor.internal-dispatcher.fork-join-executor) uses the same new default. (PR2890)pekko.remote.artery.propagate-harmless-quarantine-eventschanged fromontooff, so harmless quarantine events are no longer propagated by default. (PR2430)pekko.remote.artery.advanced.tcp-magicchanged from["AKKA", "PEKK"]to["PEKK", "AKKA"](see the TCP magic header note above). The setting first appeared in Pekko 1.7.0, with a default of["AKKA", "PEKK"]. (PR3425)- Artery compression heavy hitter detection is controlled by the new setting
pekko.remote.artery.advanced.compression.frequency-sketch-implementation, which defaults to"fast-frequency-sketch"(a smaller, aging sketch). Set it to"count-min-sketch"to restore the Pekko 1.x implementation. (PR3023) - Persistence plugins no longer use the dedicated
pekko.persistence.dispatchersby default. Theplugin-dispatcherandreplay-dispatchersettings in the journal and snapshot-store plugin fallbacks changed frompekko.persistence.dispatchers.default-plugin-dispatcher/pekko.persistence.dispatchers.default-replay-dispatchertopekko.actor.default-dispatcher. Thepekko.persistence.dispatchersdefinitions are deprecated; plugins that need a custom dispatcher should define their own. (PR2482) pekko.serialization.jackson.read.max-document-lengthandpekko.serialization.jackson.read.max-token-countchanged from-1tounlimited, a value they now accept. Negative numbers such as-1still mean unlimited, so the effective limits are unchanged and existing overrides keep working. The same applies underpekko.serialization.jackson3. (PR3522)
Changed behavior
pekko.remote.artery.ssl.rotating-keys-engine.ca-cert-filemay now bundle more than one certificate (for example a root CA and its intermediates, or several roots while a CA is being rotated); all of them are trusted. Pekko 1.x only loaded the first certificate in the file. The setting and its default are unchanged. If your CA file contains certificates you do not intend to trust, remove them before upgrading. (PR3528)
Removed configuration
pekko.actor.typed.timeoutwas removed along with the deprecatedTypedActorAPI. (PR1969)pekko.cluster.sharding.passivate-idle-entity-afterwas removed; usepekko.cluster.sharding.passivation.default-idle-strategy.idle-entity.timeoutinstead. (PR3026)- The
pekko.ssl-configand top-levelssl-configsections were removed along with thessl-configlibrary dependency. (PR2127)
New configuration
The full reference.conf for each module, with descriptions of every setting, is listed in the default configuration reference.
pekko.actor.default-dispatcher.fork-join-executor.virtual-thread-start-number,pekko.actor.default-dispatcher.thread-pool-executor.virtual-thread-start-numberandpekko.actor.default-dispatcher.virtual-thread-executor.virtual-thread-start-numbercontrol the starting id for virtual threads created by a dispatcher. (PR2242)pekko.actor.default-dispatcher.thread-pool-executor.virtualizeallows a thread-pool-executor based dispatcher to run on virtual threads (JDK 21+). The same settings were added topekko.actor.default-blocking-io-dispatcher.thread-pool-executor. (PR2169)pekko.scheduled-clock-intervalcontrols how frequently the clock used by recency-based passivation strategies is updated. (PR2766)- The async DNS resolver actors now run on the DNS dispatcher via the new deployment entries
pekko.actor.deployment."/IO-DNS/async-dns/*"and (in pekko-discovery)pekko.actor.deployment."/SD-DNS/async-dns/*". (PR2895) pekko.actor.serialization-max-nesting-depth(default32) bounds the nesting depth when a serialized payload encloses another serialized payload. First appeared in Pekko 1.7.1. (PR3500)pekko.serialization.max-decompressed-size(defaultunlimited) bounds the size a gzipped payload may expand to when the cluster, cluster-metrics, cluster-sharding, cluster-tools and distributed-data serializers decompress it. First appeared in Pekko 1.7.1. (PR3502)
pekko-cluster-sharding-typed, pekko-cluster-sharding and pekko-distributed-data:
pekko.cluster.sharded-daemon-process.keep-alive-from-number-of-nodesandpekko.cluster.sharded-daemon-process.keep-alive-throttle-intervaltune keep-alive pinging, which is now performed from a limited number of nodes instead of every node. (PR2734)pekko.cluster.sharding.healthcheck.disabled-afterdisables the sharding health check after the configured duration post member-up. (PR2785)pekko.cluster.distributed-data.expire-keys-after-inactivityconfigures automatic expiry of inactive Distributed Data keys. (PR2733)
pekko-persistence, pekko-persistence-typed and pekko-persistence-query:
pekko.persistence.query.events-by-slice-firehoseis a new section configuring the events-by-slice firehose query that fans out one shared journal query to many consumers. (PR3277)pekko.persistence.typed.event-writer.max-batch-sizeandpekko.persistence.typed.event-writer.ask-timeoutconfigure the event writer. (PR3432)pekko.persistence.typed.event-writer.fill-sequence-number-gaps(defaultoff) makes the event writer fill gaps in sequence numbers withFilteredPayloadevents, andpekko.persistence.typed.event-writer.latest-sequence-number-cache-capacity(default1000) sets how many persistence ids it keeps the latest sequence number in memory for when gap filling is enabled. (PR3653)replay-batch-sizein the journal plugin fallback bounds the number of replayed events queued in a recovering persistent actor’s mailbox. (PR3325)only-one-snapshotin the snapshot-store plugin fallback enables retention optimizations for snapshot stores that only keep the latest snapshot. (PR2767)pekko.persistence.journal.inmem.delay-writescan add an artificial write delay in tests. (PR3432)
pekko-remote classic and pekko-remote Artery:
pekko.remote.classic.passive-connection-buffer-sizebounds message buffering during a passive connection handoff. (PR3361)pekko.remote.classic.netty.ssl.security.hostname-verificationenables TLS hostname verification for classic remoting. (PR3164)pekko.remote.artery.advanced.shutdown-streams-timeoutbounds the graceful drain of Artery streams during shutdown. (PR3317)pekko.remote.artery.ssl.rotating-keys-engine.keystore-passwordsets the password for the in-memory keystore used to wrap PEM-loaded keys; override it in production. (PR3397)
pekko.serialization.jackson.compression.max-decompressed-size(defaultunlimited) bounds the size of a payload after decompression. The same setting exists underpekko.serialization.jackson3. First appeared in Pekko 1.7.1. (PR3491, PR3515)- The new
pekko-serialization-jackson3module (based on Jackson 3) is configured underpekko.serialization.jackson3, mirroring thepekko.serialization.jacksonsettings. (PR2348)
pekko.stream.materializer.stage-errors-default-log-levelcontrols the log level used for stream stage errors. (PR2805)pekko.stream.materializer.stage-actor-drain-batchbounds the number of stage-actor messages drained per envelope for lazily materialized stage actors. (PR3035)pekko.stream.materializer.tls.engineselects the stream TLS engine implementation ("legacy-actor"or the opt-in"graph-stage"). (PR2878)
Serialization bindings
These serialization changes matter if you do a rolling update from Pekko 1.x to 2.x, or need to roll back:
org.apache.pekko.util.ByteString$ByteString2, a newByteStringimplementation, is bound to the existingprimitive-bytestringserializer. That serializer writes no manifest, so Pekko 1.x nodes read these messages as an ordinaryByteString. (PR2924)- The
typed-shardingserializer is now bound to the internalClusterShardingTypedSerializablemarker instead ofShardingEnvelope.ShardingEnvelopeis serialized as before, but the serializer also handles new Sharded Daemon Process messages (its state, and the commands for getting and changing the number of processes) that Pekko 1.x nodes cannot deserialize. (PR2755) - When remoting is enabled and ScalaPB is on the classpath, ScalaPB generated messages (
scalapb.GeneratedMessage) are now bound to theprotoserializer by default. If your ScalaPB messages were serialized with another serializer in Pekko 1.x, mixed-version nodes would disagree during a rolling update. To keep the Pekko 1.x behavior, disable the new binding:pekko.actor.serialization-bindings { "scalapb.GeneratedMessage" = none }. (PR3651, PR3665) - The new
FilteredPayloadplaceholder, which is stored in the journal in place of a filtered event (for example with events-by-slice queries and projections), has a new serializer with identifier34. Pekko 1.x cannot deserialize it, so do not run Pekko 1.x against a journal once such events have been written. (PR3275) - The same serializer also handles the new
SerializedEvent, used to write events that are already serialized. Pekko 1.x cannot deserialize that either, so do not sendSerializedEventmessages to Pekko 1.x nodes during a rolling update. (PR3650) pekko-serialization-jackson(andpekko-serialization-jackson3) now provide predefinedJsonSerializableandCborSerializablemarker traits, bound by default tojackson-jsonandjackson-cbor. Your own marker traits and bindings keep working. If you switch messages to the predefined traits, Pekko 1.x nodes cannot load those classes, so only do that once no Pekko 1.x nodes remain in the cluster. (PR3649)- Only one of
pekko-serialization-jacksonandpekko-serialization-jackson3can be on the classpath. Both register serializers with the same names and identifiers, and the serializers now fail at ActorSystem startup if both modules are present. The two modules have different default setups (Jackson 2 and Jackson 3 differ in their default features and registered modules), so the same message can be serialized differently, and data written by one may not be read correctly by the other. In a Cluster or Remoting setup, make sure all Pekko nodes use the same Jackson serializer module. (PR3673)