Lagom 1.4 Migration Guide

§Lagom 1.4 Migration Guide

This guide explains how to migrate from Lagom 1.3 to Lagom 1.4. If you are upgrading from an earlier version, be sure to review previous migration guides.

Lagom 1.4 also updates to the latest major versions of Play (2.6) and Akka (2.5). We have highlighted the changes that are relevant to most Lagom users, but you may need to change code in your services that uses the Play and Akka APIs directly. You’ll also need to update any Play services in your Lagom project repositories to be compatible with Play 2.6. Please refer to the Play 2.6 migration guide and the Akka 2.5 migration guide for more details.

§Build changes

§Maven

If you’re using a lagom.version property in the properties section of your root pom.xml, then simply update that to 1.4.0. Otherwise, you’ll need to go through every place that a Lagom dependency, including plugins, is used, and set the version there.

§sbt

The version of Lagom can be updated by editing the project/plugins.sbt file, and updating the version of the Lagom sbt plugin. For example:

addSbtPlugin("com.lightbend.lagom" % "lagom-sbt-plugin" % "1.4.0")

Lagom 1.4.0 also requires Sbt 0.13.16 or later (recommended sbt 1.x). If your existing project is using a previous version of Sbt, you will need to upgrade it by editing the project/build.properties file. For example:

sbt.version=1.1.6

We also recommend you upgrade your sbt launcher.

§Scala 2.12 support

Lagom is now cross compiled to Scala 2.11 and 2.12. It’s recommended to upgrade to Scala 2.12 whenever possible, even if you are writing your Lagom services only in Java. The Scala 2.12 version of Lagom benefits from the improved optimizer and use of Java 8 features in the new version of the Scala compiler, resulting in a leaner, faster Lagom for everyone.

§Maven

Maven users will need to update all Scala libraries in the project. A Scala library has typically its Scala major version appended to the artifact ID, for example: lagom-javadsl-api_2.12; where _2.12 indicates the major Scala version used to compile that library. Make sure to replace all references to _2.11 by _2.12.

Alternatively, you can consider adding a maven property to your parent pom file…

  <properties>
      <scala.binary.version>2.12</scala.binary.version>
      <lagom.version>1.4.0</lagom.version>
  </properties>

and use it when declaring dependencies, for example:

  <dependency>
      <groupId>com.lightbend.lagom</groupId>
      <artifactId>lagom-javadsl-api_${scala.binary.version}</artifactId>
      <version>${lagom.version}</version>
  </dependency>

§sbt

The Scala version can be updated by editing the build.sbt file, and updating the scalaVersion settings, for example:

scalaVersion in ThisBuild := "2.12.9"

§Akka HTTP as the default server engine

Play 2.6 introduces a new default server engine implemented using Akka HTTP instead of Netty.

You can read more in the Play documentation at Akka HTTP Server Backend.

§Selecting the server engine in sbt

Lagom 1.4 now defaults to using the Akka HTTP server when using sbt. Once you upgrade the version of the Lagom sbt plugin in project/plugins.sbt, you won’t need to make any other changes to use it.

If you need to change back to Netty, you have to explicitly disable the LagomAkkaHttpServer plugin and enable the LagomNettyServer plugin. Note that the LagomAkkaHttpServer plugin is added by default to any LagomJava or LagomScala project.

lazy val `inventory-service-impl` = (project in file("inventory-impl"))
  .enablePlugins(LagomJava, LagomNettyServer) // Adds LagomNettyServer
  .disablePlugins(LagomAkkaHttpServer)        // Removes LagomAkkaHttpServer
  .settings( /* ... */ )
  .dependsOn(`inventory-api`)

§Selecting the server engine in Maven

Maven users will need to explicitly migrate each service to the new Akka HTTP server. If you check each service’s pom.xml you’ll notice a dependency to play-netty-server_2.12. To replace the Netty HTTP backend with the new Akka HTTP backend remove the dependency to play-netty-server_2.12 and add a dependency to play-akka-http-server_2.12 like in the following example.

        <dependency>
            <groupId>com.typesafe.play</groupId>
            <!--<artifactId>play-netty-server_2.12</artifactId>-->
            <artifactId>play-akka-http-server_2.12</artifactId>
        </dependency>

§Deprecations

§Configuration API

The class play.Configuration was deprecated in favor of using Typesafe Config directly. So, instead of using play.Configuration you must now use com.typesafe.config.Config. For more details, see the Play documentation: Java Configuration API Migration.

§Binding services

Binding multiple Lagom service descriptors in one Lagom service has been deprecated. If you are currently binding multiple Lagom service descriptors in one Lagom service, you should combine these into one. The reason for this change is that we found most microservice deployment platforms simply don’t support having multiple names for the one service, hence a service that serves multiple service descriptors, each with their own name, would not be compatible with those environments.

Consequently, we have deprecated the methods for binding multiple service descriptors. To migrate, in your Guice module that binds your services, change the following code:

bindServices(serviceBinding(MyService.class, MyServiceImpl.class));

to:

bindService(MyService.class, MyServiceImpl.class);

§Cassandra persistence

Lagom 1.4 requires each service that uses Cassandra persistence to define three keyspace configuration settings in application.conf:

cassandra-journal.keyspace = my_service_journal
cassandra-snapshot-store.keyspace = my_service_snapshot
lagom.persistence.read-side.cassandra.keyspace = my_service_read_side

While different services should be isolated by using different keyspaces, it is perfectly fine to use the same keyspace for all of these components within one service. In that case, it can be convenient to define a custom keyspace configuration property and use property substitution to avoid repeating it.

my-service.cassandra.keyspace = my_service

cassandra-journal.keyspace = ${my-service.cassandra.keyspace}
cassandra-snapshot-store.keyspace = ${my-service.cassandra.keyspace}
lagom.persistence.read-side.cassandra.keyspace = ${my-service.cassandra.keyspace}

If you are using macOS or Linux, or have other access to a bash scripting environment, running this shell script in your base project directory can help add this configuration to existing services:

#!/bin/bash

set -eu

for svc_dir in *-impl; do
    svc=${svc_dir%-impl}

    # change $svc_dir to $svc on the next line to leave out the _impl suffix
    keyspace=$(tr - _ <<<$svc_dir)
    cat >> $svc_dir/src/main/resources/application.conf <<END
$svc.cassandra.keyspace = $keyspace

cassandra-journal.keyspace = \${$svc.cassandra.keyspace}
cassandra-snapshot-store.keyspace = \${$svc.cassandra.keyspace}
lagom.persistence.read-side.cassandra.keyspace = \${$svc.cassandra.keyspace}
END
done

Previous versions of Lagom automatically calculated a default Cassandra keyspace for each service, based on the name of the service project, and injected this keyspace configuration in development mode. When running in production, these calculated keyspaces were not used, resulting in multiple services sharing the same keyspaces by default.

In Lagom 1.4, services that use Cassandra persistence will fail on startup when these properties are not defined.

See Storing Persistent Entities in Cassandra for more details.

§RDBMS persistence

If you are using Lagom’s Persistence API with a relational database, you will need to add an index to your journal table.

The relational database support is based on the akka-persistence-jdbc plugin. This plugin was updated to version 3.1.0, which includes an important bug fix that requires a new column index. If you do not update your database schema, it will result in degraded performance when querying events.

Below you will find the index creation statement for each supported database.

§Postgres

CREATE UNIQUE INDEX journal_ordering_idx ON public.journal(ordering);

§MySQL

CREATE UNIQUE INDEX journal_ordering_idx ON journal(ordering);

§Oracle

CREATE UNIQUE INDEX "journal_ordering_idx" ON "journal"("ordering")

§H2 Database (for use in development only)

CREATE UNIQUE INDEX "journal_ordering_idx" ON PUBLIC."journal"("ordering");

From version 3.0.0, the akka-persistence-jdbc Persistence Query implementation treats the offset as exclusive instead of inclusive, matching the behavior of the Cassandra implementation. Most Lagom users will not be affected by this change. Previous versions of Lagom compensated for the different behavior of the Cassandra and JDBC implementations automatically, and this change allowed that workaround to be removed. This will only impact you if you were using the Akka Persistence Query API directly.

The new version of akka-persistence-jdbc has a backward-incompatible change to the ReadJournalDao API. This is not being used by Lagom and most Lagom users will not be impacted by this. However, if you have implemented a DAO extending ReadJournalDao, you will need to migrate your code manually. Details can be found on GitHub in the pull request containing the change.

§Development mode Service Locator

In previous versions, Lagom’s development mode Service Locator has listened on port 8000. Because port 8000 is a common port on which apps listen, this caused conflicts for some Lagom users. In Lagom 1.4, its default port has been changed to 9008. This can be changed if needed in your build configuration. See the documentation on the Service Locator default port for details.

§Upgrading a production system

Lagom 1.4 introduces a few new features and changes that you must be aware before upgrading a clustered production system. Note, this is only relevant if you are using clustering. Clustering is enabled in Lagom when using Persistence or PubSub APIs, or if you have used it directly.

§Background information

Akka 2.5 introduces a new state storage mode for sharding data, which is a feature used by the Persistence layer. This new sharding state storage mode is based on Conflict Free Replicated Data Types (CRDTs) and it’s named distributed-data, ddata for short. This is incompatible with the previous mode (persistence). Mixing modes in a cluster will corrupt your event journal. Therefore, we are keeping the persistence mode as the default in Lagom.

For more information about the state storage mode, see Distributed Data vs. Persistence Mode in the Akka documentation.

Moreover, some of the internal messages used by Lagom have new serializers. Special attention must be taken when performing rolling upgrades as old nodes in the cluster may not be able to deserialize some messages.

Depending on whether you are planning a rolling upgrade or downtime upgrade, you will need to take different actions.

§Rolling upgrade

Rolling upgrades can be safely performed if, and only if, you migrate your cluster from Lagom 1.3.10 to Lagom 1.4. If your system is using any previous version of Lagom, you will first need to upgrade it to 1.3.10. Make sure you read and understand any intermediary migration guide.

As mentioned above, Lagom 1.4 is not using the new ddata mode for sharding data storage and some new messages serializers are disabled. This will allow you to perform rolling upgrades without any risk (assuming your current version is 1.3.10).

Other than that difference, refer to the Akka Rolling Update documentation for more detailed information on important considerations when performing a rolling upgrade.

§Downtime upgrade

If your application can tolerate a one time only downtime upgrade, we recommend you to enable ddata and the new serializers for akka.Done, akka.actor.Address and akka.remote.UniqueAddress. Once this upgrade is complete, further downtime is not required.

In order to achieve this, make sure you have added the following properties to your application.conf file.

# Enable new sharding state store mode by overriding Lagom's default
akka.cluster.sharding.state-store-mode = ddata

# Enable serializers provided in Akka 2.5.8+ to avoid the use of Java serialization.
akka.actor.serialization-bindings {
    "akka.Done"                 = akka-misc
    "akka.actor.Address"        = akka-misc
    "akka.remote.UniqueAddress" = akka-misc
}

§ConductR

ConductR users must update to conductr-lib 2.1.1 or later (2.2.0 recommended) for full compatibility with Lagom 1.4.0.

You can find more information in the conductr-lib README file.

§Updating ConductR with sbt

Edit the project/plugins.sbt file to update sbt-conductr to version 2.5.1 or later (2.6.0 recommended):

addSbtPlugin("com.lightbend.conductr" % "sbt-conductr" % "2.6.0")

This automatically includes the correct version of conductr-lib.

§Updating ConductR with Maven

Update each pom.xml that includes a dependency on conductr-bundle-lib:

<dependency>
    <groupId>com.typesafe.conductr</groupId>
    <artifactId>lagom14-java-conductr-bundle-lib_2.12</artifactId>
    <version>2.2.0</version>
</dependency>

Note that, in addition to updating the version, there is a new artifact ID for the Lagom 1.4 compatible module.

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.