Migration Guide 2.4.x to 2.5.x - Version 2.5-M1

Migration Guide 2.4.x to 2.5.x

Actor (Java)

AbstractActor

AbstractActor has been promoted from its experimental state and while doing this we did some small, but important, improvements to the API that will require some mechanical changes of your source code.

Previously the receive behavior was set with the receive method, but now an actor has to define its initial receive behavior by implementing the createReceive method in the AbstractActor. This has the advantages:

  • It gives a clear entry point of what to implement. The compiler tells you that the abstract method must be implemented.
  • It's impossible to forget to set the receive behavior.
  • It's not possible to define the receive behavior more than once.

The return type of createReceive is AbstractActor.Receive. It defines which messages your Actor can handle, along with the implementation of how the messages should be processed. You can build such behavior with a builder named ReceiveBuilder.

AbstractActor.Receive can also be used in getContext().become.

The old receive method exposed Scala's PartialFunction and BoxedUnit in the signature, which are unnecessary concepts for newcomers to learn. The new createReceive requires no additional imports.

Note that The Receive can still be implemented in other ways than using the ReceiveBuilder since it in the end is just a wrapper around a Scala PartialFunction. For example, one could implement an adapter to Javaslang Pattern Matching DSL.

The mechanical source code change for migration to the new AbstractActor is to implement the createReceive instead of calling receive (compiler will tell that this is missing).

Old:

import akka.actor.AbstractActor;
import akka.japi.pf.ReceiveBuilder;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;

public class SomeActor extends AbstractActor {
  public SomeActor() {
    receive(ReceiveBuilder
      .match(String.class, s -> System.out.println(s.toLowerCase())).
      .build());
  }
}

New:

import akka.actor.AbstractActor;

public class SomeActor extends AbstractActor {
  @Override
  public Receive createReceive() {
    return receiveBuilder()
      .match(String.class, s -> System.out.println(s.toLowerCase()))
      .build();
  }
}

See Receive messages documentation for more advice about how to implement createReceive.

A few new methods have been added with deprecation of the old. Worth noting is preRestart.

Old:

@Override
public void preRestart(Throwable reason, scala.Option<Object> message) {
  super.preRestart(reason, message);
}

New:

@Override
public void preRestart(Throwable reason, java.util.Optional<Object> message) {
  super.preRestart(reason, message);
}

AbstractPersistentActor

Similar change as described above for AbstractActor is needed for AbstractPersistentActor. Implement createReceiveRecover instead of receiveRecover, and createReceive instead of receiveCommand.

Old:

@Override
public PartialFunction<Object, BoxedUnit> receiveCommand() {
  return ReceiveBuilder.
    match(String.class, cmd -> {/* ... */}).build();
}

@Override
public PartialFunction<Object, BoxedUnit> receiveRecover() {
  return ReceiveBuilder.
      match(String.class, evt -> {/* ... */}).build();
}

New:

@Override
public Receive createReceive() {
  return receiveBuilder().
    match(String.class, cmd -> {/* ... */}).build();
}

@Override
public Receive createReceiveRecover() {
  return receiveBuilder().
      match(String.class, evt -> {/* ... */}).build();
}

UntypedActor

UntypedActor has been deprecated in favor of AbstractActor. As a migration path you can extend UntypedAbstractActor instead of UntypedActor.

Old:

import akka.actor.UntypedActor;

public class SomeActor extends UntypedActor {

  public static class Msg1 {}

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof Msg1) {
      Msg1 msg1 = (Msg1) msg;
      // actual work
    } else {
      unhandled(msg);
    }
  }
}

New:

import akka.actor.UntypedAbstractActor;

public class SomeActor extends UntypedAbstractActor {

  public static class Msg1 {}

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof Msg1) {
      Msg1 msg1 = (Msg1) msg;
      // actual work
    } else {
      unhandled(msg);
    }
  }
}

It's recommended to migrate UntypedActor to AbstractActor by implementing createReceive instead of onMessage.

Old:

import akka.actor.UntypedActor;

public class SomeActor extends UntypedActor {

  @Override
  public void onReceive(Object msg) throws Exception {
    if (msg instanceof String) {
      String s = (String) msg;
      System.out.println(s.toLowerCase());
    } else {
      unhandled(msg);
    }
  }
}

New:

import akka.actor.AbstractActor;

public class SomeActor extends AbstractActor {
  @Override
  public Receive createReceive() {
    return receiveBuilder()
      .match(String.class, s -> {
        System.out.println(s.toLowerCase());
      })
      .build();
  }
}

See Receive messages documentation for more advice about how to implement createReceive.

Similar with UntypedActorWithStash, UntypedPersistentActor, and UntypedPersistentActorWithAtLeastOnceDelivery.

Actor (Scala)

Actor DSL deprecation

Actor DSL is a rarely used feature and thus will be deprecated and removed. Use plain system.actorOf instead of the DSL to create Actors if you have been using it.

Streams

Removal of StatefulStage, PushPullStage

StatefulStage and PushPullStage were first introduced in Akka Streams 1.0, and later deprecated and replaced by GraphStage in 2.0-M2. The GraphStage API has all features (and even more) as the previous APIs and is even nicer to use.

Please refer to the GraphStage documentation :ref:` for Scala <graphstage-scala>` or the documentation for Java, for details on building custom GraphStages.

StatefulStage would be migrated to a simple GraphStage that contains some mutable state in its GraphStageLogic, and PushPullStage directly translate to graph stages.

Removal of Source.transform, replaced by via

Along with the removal of Stage (as described above), the transform methods creating Flows/Sources/Sinks from Stage have been removed. They are replaced by using GraphStage instances with via, e.g.:

exampleFlow.transform(() => new MyStage())

would now be:

myFlow.via(new MyGraphStage)

as the GraphStage itself is a factory of logic instances.

Deprecation of ActorSubscriber and ActorPublisher

The classes ActorPublisher and ActorSubscriber were the first user-facing Reactive Streams integration API that we provided for end-users. Akka Streams APIs have evolved and improved a lot since then, and now there is no need to use these low-level abstractions anymore. It is easy to get things wrong when implementing them, and one would have to validate each implementation of such Actor using the Reactive Streams Technology Compatibility Kit.

The replacement API is the powerful GraphStage. It has all features that raw Actors provided for implementing Stream stages and adds additional protocol and type-safety. You can learn all about it in the documentation: stream-customize-scala`and :ref:`Custom stream processing in JavaDSL.

You should also read the blog post series on the official team blog, starting with Mastering GraphStages, part I, which explains using and implementing GraphStages in more practical terms than the reference documentation.

Remote

Mutual TLS authentication now required by default for netty-based SSL transport

Mutual TLS authentication is now required by default for the netty-based SSL transport.

Nodes that are configured with this setting to on might not be able to receive messages from nodes that run on older versions of akka-remote. This is because in versions of Akka < 2.4.12 the active side of the remoting connection will not send over certificates even if asked to.

It is still possible to make a rolling upgrade from a version < 2.4.12 by doing the upgrade stepwise:
  • first, upgrade Akka to the latest version but keep akka.remote.netty.ssl.require-mutual-authentication at off and do a first rolling upgrade
  • second, turn the setting to on and do another rolling upgrade

For more information see the documentation for the akka.remote.netty.ssl.require-mutual-authentication configuration setting in akka-remote's reference.conf.

additional-serialization-bindings

From Akka 2.5.0 the additional-serialization-bindings are enabled by default. That defines serializers that are replacing some Java serialization that were used in 2.4. This setting was disabled by default in Akka 2.4.16 but can also be enabled in an Akka 2.4 system.

To still be able to support rolling upgrade from a system with this setting disabled, e.g. default for 2.4.16, it is possible to disable the additional serializers and continue using Java serialization for those messages.

akka.actor {
  # Set this to off to disable serialization-bindings define in
  # additional-serialization-bindings. That should only be needed
  # for backwards compatibility reasons.
  enable-additional-serialization-bindings = off
}

Please note that this setting must be the same on all nodes participating in a cluster, otherwise the mis-aligned serialization configurations will cause deserialization errors on the receiving nodes.

Wire Protocol Compatibility

It is possible to use Akka Remoting between nodes running Akka 2.4.16 and 2.5-M1, but some settings have changed so you might need to adjust some configuration as described in Rolling Update.

Cluster

Rolling Update

It is possible to do a rolling update from Akka 2.4.16 to 2.5-M1, i.e. running a cluster of 2.4.16 nodes and join nodes running 2.5-M1 followed by shutting down the old nodes.

You must first update all nodes to 2.4.16. It's not supported to update directly from an older version than 2.4.16 to 2.5-M1. For example, if you are running 2.4.11 you must first do a rolling update to 2.4.16, shut down all 2.4.11 nodes, and then do the rolling update to 2.5-M1.

For some configuration settings it's important to use the same values on all nodes in the cluster. Some settings have changed default value in 2.5-M1 and therefore you need to review your configuration before doing a rolling update to 2.5-M1. Such settings are mentioned elsewhere in this migration guide and here is a summary of things to consider.

Coordinated Shutdown

There is a new extension named CoordinatedShutdown that will stop certain actors and services in a specific order and perform registered tasks during the shutdown process.

When using Akka Cluster, tasks for graceful leaving of cluster including graceful shutdown of Cluster Singletons and Cluster Sharding are now performed automatically.

Previously it was documented that things like terminating the ActorSystem should be done when the cluster member was removed, but this was very difficult to get right. That is now taken care of automatically. This might result in changed behavior, hopefully to the better. It might also be in conflict with your previous shutdown code so please read the documentation for the Coordinated Shutdown and revisit your own implementations. Most likely your implementation will not be needed any more or it can be simplified.

More information can be found in the documentation for Scala or documentation for Java

For some tests it might be undesired to terminate the ActorSystem via CoordinatedShutdown. You can disable that by adding the following to the configuration of the ActorSystem that is used in the test:

# Don't terminate ActorSystem via CoordinatedShutdown in tests
akka.coordinated-shutdown.terminate-actor-system = off
akka.coordinated-shutdown.run-by-jvm-shutdown-hook = off
akka.cluster.run-coordinated-shutdown-when-down = off

WeaklyUp

WeaklyUp Members is now enabled by default, but it can be disabled with configuration option:

akka.cluster.allow-weakly-up-members = off

You should not run a cluster with this feature enabled on some nodes and disabled on some. Therefore you might need to enable/disable it in configuration when performing rolling upgrade from 2.4.x to 2.5.0.

Cluster Sharding state-store-mode

Distributed Data mode is now the default state-store-mode for Cluster Sharding. The persistence mode is also supported. Read more in the documentation for Scala or the documentation for Java.

It's important to use the same mode on all nodes in the cluster, i.e. if you perform a rolling upgrade from 2.4.16 you might need to change the state-store-mode to be the same (persistence is default in 2.4.x):

akka.cluster.sharding.state-store-mode = persistence

Note that the stored Remembering Entities data with persistence mode cannot be migrated to the data mode. Such entities must be started again in some other way when using ddata mode.

Cluster Management Command Line Tool

There is a new cluster management tool with HTTP API that has the same functionality as the command line tool. The HTTP API gives you access to cluster membership information as JSON including full reachability status between the nodes. It supports the ordinary cluster operations such as join, leave, and down.

See documentation of akka/akka-cluster-management.

The command line script for cluster management has been deprecated and is scheduled for removal in the next major version. Use the HTTP API with curl or similar instead.

Distributed Data

Map allow generic type for the keys

In 2.4 the key of any Distributed Data map always needed to be of type String. In 2.5 you can use any type for the key. This means that every map (ORMap, LWWMap, PNCounterMap, ORMultiMap) now takes an extra type parameter to specify the key type. To migrate existing code from 2.4 to 2.5 you simple add String as key type, for example: ORMultiMap[Foo] becomes ORMultiMap[String, Foo]. PNCounterMap didn't take a type parameter in version 2.4, so PNCounterMap in 2.4 becomes PNCounterMap[String] in 2.5. Java developers should use <> instead of [], e.g: PNCounterMap<String>.

NOTE: Even though the interface is not compatible between 2.4 and 2.5, the binary protocol over the wire is (as long as you use String as key type). This means that 2.4 nodes can synchronize with 2.5 nodes.

Subscribers

When an entity is removed subscribers will not receive Replicator.DataDeleted any more. They will receive Replicator.Deleted instead.

Persistence

Removal of PersistentView

After being deprecated for a long time, and replaced by Persistence Query Java (Persistence Query Scala) PersistentView has been removed now removed.

The corresponding query type is EventsByPersistenceId. There are several alternatives for connecting the Source to an actor corresponding to a previous PersistentView. There are several alternatives for connecting the Source to an actor corresponding to a previous PersistentView actor which are documented in Integration for Scala and Java.

The consuming actor may be a plain Actor or an PersistentActor if it needs to store its own state (e.g. fromSequenceNr offset).

Please note that Persistence Query is not experimental anymore in Akka 2.5.0, so you can safely upgrade to it.

Persistence Plugin Proxy

A new persistence plugin proxy was added, that allows sharing of an otherwise non-sharable journal or snapshot store. The proxy is available by setting akka.persistence.journal.plugin or akka.persistence.snapshot-store.plugin to akka.persistence.journal.proxy or akka.persistence.snapshot-store.proxy, respectively. The proxy supplants the Shared LevelDB journal.

Persistence Query

Persistence Query has been promoted to a stable module. Only slight API changes were made since the module was introduced:

Query naming consistency improved

Queries always fall into one of the two categories: infinite or finite ("current"). The naming convention for these categories of queries was solidified and is now as follows:

  • "infinite" - e.g. eventsByTag, persistenceIds - which will keep emitting events as they are persisted and match the query.
  • "finite", also known as "current" - e.g. currentEventsByTag, currentPersistenceIds - which will complete the stream once the query completed, for the journal's definition of "current". For example in an SQL store it would mean it only queries the database once.

Only the AllPersistenceIdsQuery class and method name changed due to this. The class is now called PersistenceIdsQuery, and the method which used to be allPersistenceIds is now persistenceIds.

Queries now use Offset instead of Long for offsets

This change was made to better accomodate the various types of Journals and their understanding what an offset is. For example, in some journals an offset is always a time, while in others it is a numeric offset (like a sequence id).

Instead of the previous Long offset you can now use the provided Offset factories (and types):

  • akka.persistence.query.Offset.sequence(value: Long),
  • akka.persistence.query.Offset.timeBasedUUID(value: UUID)
  • and finally NoOffset if not offset should be used.

Journals are also free to provide their own specific Offset types. Consult your journal plugin's documentation for details.

Agents

Agents are now deprecated

Akka Agents are a very simple way of containing mutable state and allowing to access it safely from multiple threads. The abstraction is leaky though, as Agents do not work over the network (unlike Akka Actors).

As users were often confused by "when to use an Actor vs. when to use an Agent?" a decision was made to deprecate the Agents, as they rarely are really enough and do not fit the Akka spirit of thinking about distribution. We also anticipate to replace the uses of Agents by the upcoming Akka Typed, so in preparation thereof the Agents have been deprecated in 2.5.

If you use Agents and would like to take over the maintanance thereof, please contact the team on gitter or github.

Contents