Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration to Pekko and Play 3.0 #278

Merged
merged 4 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

## Changelog

### [:link: 4.0.0](https://github.com/KarelCemus/play-redis/tree/4.0.0-M1)

Migration to Pekko and Play 3.0, thanks to @TomJKing for help in [#272](https://github.com/KarelCemus/play-redis/pull/272),
finished in [#278](https://github.com/KarelCemus/play-redis/pull/278)

### [:link: 3.0.0](https://github.com/KarelCemus/play-redis/tree/3.0.0-M1)

Updated to Play `2.9.0` and dropped `Scala 2.12` since it was discontinued from the Play framework.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ operations. Besides the basic methods such as `get`, `set`
and `remove`, it provides more convenient methods such as
`expire`, `exists`, `invalidate` and much more.

The implementation builds on the top of Akka actor system,
The implementation builds on the top of Pekko actor system,
it is **completely non-blocking and asynchronous** under
the hood, though it also provides blocking APIs to ease
the use. Furthermore, the library supports several configuration
Expand All @@ -51,9 +51,9 @@ as well as on your premise.
- support of [standalone, cluster,](https://github.com/KarelCemus/play-redis/blob/3.0.0-M3/doc/20-configuration.md#standalone-vs-cluster)
[aws-cluster,](https://github.com/KarelCemus/play-redis/blob/3.0.0-M3/doc/20-configuration.md#aws-cluster)
and [sentinel modes](https://github.com/KarelCemus/play-redis/blob/3.0.0-M3/doc/20-configuration.md#sentinel)
- build on the top of Akka actors and serializers, [agnostic to the serialization mechanism](https://github.com/KarelCemus/play-redis/blob/3.0.0-M3/doc/20-configuration.md#limitation-of-data-serialization)
- build on the top of Pekko actors and serializers, [agnostic to the serialization mechanism](https://github.com/KarelCemus/play-redis/blob/3.0.0-M3/doc/20-configuration.md#limitation-of-data-serialization)
- for simplicity, it uses deprecated Java serialization by default
- it is recommended to use [Kryo library](https://github.com/romix/akka-kryo-serialization) or any other mechanism
- it is recommended to use [Kryo library](https://github.com/romix/akka-kryo-serialization) or any other mechanism


## Provided APIs
Expand Down
8 changes: 4 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ crossScalaVersions := Seq("2.13.12") //, "3.3.0"

scalaVersion := crossScalaVersions.value.head

playVersion := "2.9.0"
playVersion := "3.0.0"

libraryDependencies ++= Seq(
// play framework cache API
"com.typesafe.play" %% "play-cache" % playVersion.value % Provided,
"org.playframework" %% "play-cache" % playVersion.value % Provided,
// redis connector
"io.github.rediscala" %% "rediscala" % "1.14.0-akka",
"io.github.rediscala" %% "rediscala" % "1.14.0-pekko",
// test framework with mockito extension
"org.scalatest" %% "scalatest" % "3.2.17" % Test,
"org.scalamock" %% "scalamock" % "5.2.0" % Test,
// test module for play framework
"com.typesafe.play" %% "play-test" % playVersion.value % Test,
"org.playframework" %% "play-test" % playVersion.value % Test,
// to run integration tests
"com.dimafeng" %% "testcontainers-scala-core" % "0.41.2" % Test,
)
Expand Down
20 changes: 10 additions & 10 deletions doc/20-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ it uses `JavaSerializer` by default.
Since Akka 2.4.1, default `JavaSerializer` is [officially considered inefficient for production use](https://github.com/akka/akka/pull/18552).
Nevertheless, to keep things simple, play-redis **still uses this inefficient serializer NOT to enforce** any serialization
library to end users. Although, it recommends [kryo serializer](https://github.com/romix/akka-kryo-serialization) claiming
great performance and small output stream. Any serialization library can be smoothly connected through Akka
configuration, see the [official Akka documentation](https://doc.akka.io/docs/akka/current/scala/serialization.html).
great performance and small output stream. Any serialization library can be smoothly connected through Pekko
configuration, see the [official Pekko documentation](https://pekko.apache.org/docs/pekko/current/serialization.html).


## Overview
Expand All @@ -366,11 +366,11 @@ configuration, see the [official Akka documentation](https://doc.akka.io/docs/ak

### Instance-specific (can be locally overridden)

| Key | Type | Default | Description |
|----------------------------------------------------------|---------:|--------------------------------:|-------------------------------------|
| [play.cache.redis.source](#standalone-vs-cluster) | String | `standalone` | Defines the source of the configuration. Accepted values are `standalone`, `cluster`, `connection-string`, and `custom` |
| [play.cache.redis.sync-timeout](#timeout) | Duration | `1s` | conversion timeout applied by `SyncAPI` to convert `Future[T]` to `T`|
| [play.cache.redis.redis-timeout](#timeout) | Duration | `null` | waiting for the response from redis server |
| [play.cache.redis.prefix](#namespace-prefix) | String | `null` | optional namespace, i.e., key prefix |
| play.cache.redis.dispatcher | String | `akka.actor.default-dispatcher` | Akka actor |
| [play.cache.redis.recovery](#recovery-policy) | String | `log-and-default` | Defines behavior when command execution fails. For accepted values and more see |
| Key | Type | Default | Description |
|----------------------------------------------------------|---------:|-------------------------------------:|-------------------------------------------------------------------------------------------------------------------------|
| [play.cache.redis.source](#standalone-vs-cluster) | String | `standalone` | Defines the source of the configuration. Accepted values are `standalone`, `cluster`, `connection-string`, and `custom` |
| [play.cache.redis.sync-timeout](#timeout) | Duration | `1s` | conversion timeout applied by `SyncAPI` to convert `Future[T]` to `T` |
| [play.cache.redis.redis-timeout](#timeout) | Duration | `null` | waiting for the response from redis server |
| [play.cache.redis.prefix](#namespace-prefix) | String | `null` | optional namespace, i.e., key prefix |
| play.cache.redis.dispatcher | String | `pekko.actor.default-dispatcher` | Pekko actor |
| [play.cache.redis.recovery](#recovery-policy) | String | `log-and-default` | Defines behavior when command execution fails. For accepted values and more see |
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.7
sbt.version=1.9.8
2 changes: 1 addition & 1 deletion src/main/java/play/cache/redis/AsyncCacheApi.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package play.cache.redis;

import akka.Done;
import org.apache.pekko.Done;

import java.util.Arrays;
import java.util.List;
Expand Down
8 changes: 4 additions & 4 deletions src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,13 @@ play.cache.redis {
#
prefix: null

# akka dispatcher
# pekko dispatcher
#
# note: this is global definition, can be locally overriden for each
# cache instance. To do so, redefine this property
# under 'play.cache.redis.instances.instance-name.this-property'.
#
dispatcher: akka.actor.default-dispatcher
dispatcher: pekko.actor.default-dispatcher

# invocation policy applies in methods `getOrElse`. It determines
# whether to wait until the `set` completes or return eagerly the
Expand Down Expand Up @@ -288,9 +288,9 @@ play.cache.redis {
}

# ==================
# Akka configuration
# Pekko configuration
# ==================
akka {
pekko {

actor {
serialization-bindings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import play.api.{Configuration, Environment}
* from configuration package</p>
*/
trait RedisCacheComponents {
implicit def actorSystem: akka.actor.ActorSystem
implicit def actorSystem: org.apache.pekko.actor.ActorSystem
implicit def applicationLifecycle: ApplicationLifecycle
def configuration: Configuration
def environment: Environment
Expand All @@ -27,14 +27,14 @@ trait RedisCacheComponents {
/** override this for providing a custom redis instance resolver */
implicit def redisInstanceResolver: RedisInstanceResolver = emptyInstanceResolver

private lazy val akkaSerializer: connector.AkkaSerializer = new connector.AkkaSerializerProvider().get
private lazy val pekkoSerializer: connector.PekkoSerializer = new connector.PekkoSerializerProvider().get

private lazy val manager = configuration.get("play.cache.redis")(play.api.cache.redis.configuration.RedisInstanceManager)

/** translates the cache name into the configuration */
private def redisInstance(name: String)(implicit resolver: RedisInstanceResolver): RedisInstance = manager.instanceOf(name).resolved(resolver)

private def cacheApi(instance: RedisInstance): impl.RedisCaches = new impl.RedisCachesProvider(instance, akkaSerializer, environment).get
private def cacheApi(instance: RedisInstance): impl.RedisCaches = new impl.RedisCachesProvider(instance, pekkoSerializer, environment).get

def cacheApi(name: String)(implicit resolver: RedisInstanceResolver): RedisCaches = cacheApi(redisInstance(name)(resolver))
}
6 changes: 3 additions & 3 deletions src/main/scala/play/api/cache/redis/RedisCacheModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class RedisCacheModule extends Module {
// common settings
val commons = Seq(
// bind serializer
bind[connector.AkkaSerializer].toProvider[connector.AkkaSerializerProvider],
bind[connector.PekkoSerializer].toProvider[connector.PekkoSerializerProvider],
bind[configuration.RedisInstanceResolver].to[GuiceRedisInstanceResolver],
)
// bind recovery resolver
Expand Down Expand Up @@ -108,10 +108,10 @@ class GuiceRedisCacheProvider(instance: RedisInstanceProvider) extends Provider[

override lazy val get: RedisCaches = new impl.RedisCachesProvider(
instance = instance.resolved(bind[configuration.RedisInstanceResolver]),
serializer = bind[connector.AkkaSerializer],
serializer = bind[connector.PekkoSerializer],
environment = bind[play.api.Environment],
)(
system = bind[akka.actor.ActorSystem],
system = bind[org.apache.pekko.actor.ActorSystem],
lifecycle = bind[ApplicationLifecycle],
recovery = bind[RecoveryPolicyResolver],
).get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import play.api.ConfigLoader

/**
* Configures non-connection related settings of redis instance, e.g.,
* synchronization timeout, Akka dispatcher, and recovery policy.
* synchronization timeout, Pekko dispatcher, and recovery policy.
*/
trait RedisSettings {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package play.api.cache.redis.connector

import akka.actor.ActorSystem
import akka.serialization._
import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.serialization.{Serialization, SerializationExtension}
import play.api.cache.redis._

import java.util.Base64
Expand All @@ -13,7 +13,7 @@ import scala.util._
* Provides a encode and decode methods to serialize objects into strings and
* vise versa.
*/
trait AkkaSerializer {
trait PekkoSerializer {

/**
* Method accepts a value to be serialized into the string. Based on the
Expand Down Expand Up @@ -44,20 +44,20 @@ trait AkkaSerializer {
}

/**
* Akka encoder provides implementation of serialization using Akka serializer.
* The implementation considers all primitives, nulls, and refs. This enables
* us to use Akka settings to modify serializer mapping and use different
* serializers for different objects.
* Pekko encoder provides implementation of serialization using Pekko
* serializer. The implementation considers all primitives, nulls, and refs.
* This enables us to use Pekko settings to modify serializer mapping and use
* different serializers for different objects.
*/
private[connector] class AkkaEncoder(serializer: Serialization) {
private[connector] class PekkoEncoder(serializer: Serialization) {

/** Unsafe method encoding the given value into a string */
def encode(value: Any): String = value match {
// null is special case
case null => unsupported("Null is not supported by the redis cache connector.")
// AnyVal is not supported by default, have to be implemented manually; also basic types are processed as primitives
case primitive if isPrimitive(primitive) => primitive.toString
// AnyRef is supported by Akka serializers, but it does not consider classTag, thus it is done manually
// AnyRef is supported by Pekko serializers, but it does not consider classTag, thus it is done manually
case anyRef: AnyRef => anyRefToString(anyRef)
// $COVERAGE-OFF$
// if no of the cases above matches, throw an exception
Expand All @@ -84,12 +84,12 @@ private[connector] class AkkaEncoder(serializer: Serialization) {
}

/**
* Akka decoder provides implementation of deserialization using Akka
* Pekko decoder provides implementation of deserialization using Pekko
* serializer. The implementation considers all primitives, nulls, and refs.
* This enables us to use Akka settings to modify serializer mapping and use
* This enables us to use Pekko settings to modify serializer mapping and use
* different serializers for different objects.
*/
private[connector] class AkkaDecoder(serializer: Serialization) {
private[connector] class PekkoDecoder(serializer: Serialization) {

import scala.reflect.{ClassTag => Scala}

Expand Down Expand Up @@ -139,19 +139,19 @@ private[connector] class AkkaDecoder(serializer: Serialization) {
}

@Singleton
private[connector] class AkkaSerializerImpl @Inject() (system: ActorSystem) extends AkkaSerializer {
private[connector] class PekkoSerializerImpl @Inject() (system: ActorSystem) extends PekkoSerializer {

/**
* serializer dispatcher used to serialize the objects into bytes; the
* instance is retrieved from Akka based on its configuration
* instance is retrieved from Pekko based on its configuration
*/
protected val serializer: Serialization = SerializationExtension(system)

/** value serializer based on Akka serialization */
private val encoder = new AkkaEncoder(serializer)
/** value serializer based on Pekko serialization */
private val encoder = new PekkoEncoder(serializer)

/** value decoder based on Akka serialization */
private val decoder = new AkkaDecoder(serializer)
/** value decoder based on Pekko serialization */
private val decoder = new PekkoDecoder(serializer)

/**
* Method accepts a value to be serialized into the string. Based on the
Expand Down Expand Up @@ -224,6 +224,6 @@ private[connector] object JavaClassTag {
val String: ClassTag[String] = ClassTag(classOf[String])
}

class AkkaSerializerProvider @Inject() (implicit system: ActorSystem) extends Provider[AkkaSerializer] {
lazy val get = new AkkaSerializerImpl(system)
class PekkoSerializerProvider @Inject() (implicit system: ActorSystem) extends Provider[PekkoSerializer] {
lazy val get = new PekkoSerializerImpl(system)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package play.api.cache.redis.connector

import akka.actor.{ActorSystem, Scheduler}
import org.apache.pekko.actor.{ActorSystem, Scheduler}
import play.api.Logger
import play.api.cache.redis.configuration._
import play.api.inject.ApplicationLifecycle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import scala.reflect.ClassTag
* @param redis
* implementation of the commands
*/
private[connector] class RedisConnectorImpl(serializer: AkkaSerializer, redis: RedisCommands)(implicit runtime: RedisRuntime) extends RedisConnector {
private[connector] class RedisConnectorImpl(serializer: PekkoSerializer, redis: RedisCommands)(implicit runtime: RedisRuntime) extends RedisConnector {

import ExpectedFuture._

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package play.api.cache.redis.connector

import akka.actor.ActorSystem
import org.apache.pekko.actor.ActorSystem
import play.api.cache.redis._
import play.api.inject.ApplicationLifecycle

import javax.inject.Provider

/** Provides an instance of named redis connector */
private[redis] class RedisConnectorProvider(instance: RedisInstance, serializer: AkkaSerializer)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, runtime: RedisRuntime) extends Provider[RedisConnector] {
private[redis] class RedisConnectorProvider(instance: RedisInstance, serializer: PekkoSerializer)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, runtime: RedisRuntime) extends Provider[RedisConnector] {

private[connector] lazy val commands = new RedisCommandsProvider(instance).get

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package play.api.cache.redis.connector

import akka.actor.Scheduler
import akka.pattern.after
import org.apache.pekko.actor.Scheduler
import org.apache.pekko.pattern.after
import redis._

import scala.concurrent.duration._
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/play/api/cache/redis/impl/Builders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ private object Builders {

import dsl._
import play.api.cache.redis._
import akka.pattern.AskTimeoutException
import org.apache.pekko.pattern.AskTimeoutException

trait ResultBuilder[Result[_]] {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package play.api.cache.redis.impl

import akka.Done
import org.apache.pekko.Done
import play.api.Environment
import play.api.cache.redis._

import scala.concurrent.{ExecutionContext, Future}
import scala.reflect.ClassTag

private[impl] object JavaCompatibility extends JavaCompatibilityBase {
import scala.compat.java8.{FutureConverters, OptionConverters}
import scala.jdk.javaapi.{FutureConverters, OptionConverters}

type CompletionStage[T] = java.util.concurrent.CompletionStage[T]
type Callable[T] = java.util.concurrent.Callable[T]
Expand All @@ -28,7 +28,7 @@ private[impl] object JavaCompatibility extends JavaCompatibilityBase {
}

implicit class Java8Stage[T](private val future: Future[T]) extends AnyVal {
@inline def asJava: CompletionStage[T] = FutureConverters.toJava(future)
@inline def asJava: CompletionStage[T] = FutureConverters.asJava(future)
@inline def asDone(implicit ec: ExecutionContext): Future[Done] = future.map(_ => Done)
}

Expand All @@ -41,7 +41,7 @@ private[impl] object JavaCompatibility extends JavaCompatibilityBase {
}

implicit class ScalaCompatibility[T](private val future: CompletionStage[T]) extends AnyVal {
@inline def asScala: Future[T] = FutureConverters.toScala(future)
@inline def asScala: Future[T] = FutureConverters.asScala(future)
}

implicit class RichFuture(private val future: Future.type) extends AnyVal {
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/play/api/cache/redis/impl/RedisCaches.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package play.api.cache.redis.impl

import akka.actor.ActorSystem
import org.apache.pekko.actor.ActorSystem
import play.api.Environment
import play.api.cache.redis._
import play.api.inject.ApplicationLifecycle
Expand All @@ -21,7 +21,7 @@ trait RedisCaches {
def javaAsync: play.cache.redis.AsyncCacheApi
}

private[redis] class RedisCachesProvider(instance: RedisInstance, serializer: connector.AkkaSerializer, environment: Environment)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, recovery: RecoveryPolicyResolver) extends Provider[RedisCaches] {
private[redis] class RedisCachesProvider(instance: RedisInstance, serializer: connector.PekkoSerializer, environment: Environment)(implicit system: ActorSystem, lifecycle: ApplicationLifecycle, recovery: RecoveryPolicyResolver) extends Provider[RedisCaches] {
import RedisRuntime._

implicit private lazy val runtime: RedisRuntime = RedisRuntime(instance, instance.recovery, instance.invocationPolicy, instance.prefix)(system)
Expand Down
Loading
Loading