From 5cc297dfd3a65cefab618d681cdce3409246b920 Mon Sep 17 00:00:00 2001 From: Mikhail Sokolov Date: Thu, 5 Sep 2024 16:36:49 +0300 Subject: [PATCH] Update deps, add Scala 3 support, drop Scala 2.12 Compared to conhub 1.2.1 the changes are source and binary compatible with these exceptions: - potential source compat breakage: NamedDispatcher doesn't have an implicit argument in the constructor anymore but it appears to be unused even in the internal Evolution codebase Changes highlights: - updated plugins and dependencies - added Scala 3.3.3 cross-compilation - removed Scala 2.12 support - introduced sbt-version-policy plugin and MiMa bincompat checks - fixed compilation warnings, minor test refactoring - replaced CurrentThreadExecutionContext usage with direct calls to ExecutionContext.parasitic since we do not support 2.12 anymore - Scala 3 compiler complained about NamedDispatcher implicit arguments in the constructor. Since the class is unused even in the internal Evolution codebase, implicit modifier was removed and the type was marked as deprecated. --- .github/workflows/ci.yml | 8 +-- build.sbt | 52 +++++++++++++-- project/Dependencies.scala | 30 ++++----- project/build.properties | 2 +- project/plugins.sbt | 10 +-- .../concurrent/NamedDispatcher.scala | 4 +- .../com/evolutiongaming/conhub/ConHub.scala | 5 +- .../evolutiongaming/conhub/ConHubImpl.scala | 4 +- .../conhub/ConHubSerializer.scala | 15 +++-- .../evolutiongaming/conhub/ConStates.scala | 13 ++-- .../conhub/MemberEventSubscribe.scala | 7 +- .../evolutiongaming/conhub/SendEvent.scala | 2 +- .../com/evolutiongaming/conhub/SendMsgs.scala | 10 +-- .../conhub/SequentialMapHelper.scala | 7 +- .../evolutiongaming/conhub/UpdateResult.scala | 7 +- .../conhub/transport/SendMsg.scala | 66 +++++++++---------- .../conhub/ConHubSerializerSpec.scala | 6 +- .../evolutiongaming/conhub/ConHubSpec.scala | 7 +- .../conhub/ConHubSpecHelper.scala | 8 +-- .../conhub/ConStatesSpec.scala | 14 ++-- .../evolutiongaming/conhub/SendMsgSpec.scala | 2 +- version.sbt | 2 +- 22 files changed, 165 insertions(+), 116 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4cfd268..0bfd69b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,8 @@ jobs: strategy: matrix: scala: - - 2.13.8 - - 2.12.17 + - 2.13.14 + - 3.3.3 steps: - uses: actions/checkout@v3 @@ -19,12 +19,12 @@ jobs: - uses: coursier/cache-action@v6 - name: scala - uses: olafurpg/setup-scala@v13 + uses: olafurpg/setup-scala@v14 with: java-version: 17 - name: build ${{ matrix.scala }} - run: sbt ++${{ matrix.scala }} clean coverage test + run: sbt ++${{ matrix.scala }} clean check coverage test - name: test coverage if: success() diff --git a/build.sbt b/build.sbt index 175cb62..6826791 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -import Dependencies._ +import Dependencies.* name := "conhub" @@ -14,13 +14,22 @@ organizationHomepage := Some(url("https://evolution.com")) scalaVersion := crossScalaVersions.value.head -crossScalaVersions := Seq("2.13.8", "2.12.17") +crossScalaVersions := Seq("2.13.14", "3.3.3") + +Compile / scalacOptions ++= { + if (scalaBinaryVersion.value == "2.13") { + Seq( + "-Xsource:3" + ) + } else Seq.empty +} Test / fork := true publishTo := Some(Resolver.evolutionReleases) libraryDependencies ++= Seq( + `scodec-bits`, Akka.actor, Akka.remote, Akka.cluster, @@ -38,6 +47,18 @@ libraryDependencies ++= Seq( `scala-tools` % Test, scalatest % Test) +libraryDependencies ++= { + if (scalaBinaryVersion.value == "2.13") { + Seq( + `scodec-core1`, + ) + } else { + Seq( + `scodec-core2`, + ) + } +} + licenses := Seq(("MIT", url("https://opensource.org/licenses/MIT"))) releaseCrossBuild := true @@ -46,6 +67,27 @@ Compile / doc / scalacOptions ++= Seq("-groups", "-implicits", "-no-link-warning versionScheme := Some("semver-spec") -//addCommandAlias("check", "all versionPolicyCheck Compile/doc") -addCommandAlias("check", "show version") -addCommandAlias("build", "+all compile test") +addCommandAlias("check", "+all versionPolicyCheck Compile/doc") +addCommandAlias("build", "+all test package") + +// Your next release will be binary compatible with the previous one, +// but it may not be source compatible (ie, it will be a minor release). +ThisBuild / versionPolicyIntention := Compatibility.BinaryCompatible + +/* +versionPolicyReportDependencyIssues ignored dependencies when compared to conhub 1.2.1. +All of those should not affect the library users, binary compatibility should be preserved. + +Remember to clear up after 1.3.0 release! + */ +ThisBuild / versionPolicyIgnored ++= Seq( + /* + Examples: + + //com.chuusai:shapeless_2.13: missing dependency + "com.chuusai" %% "shapeless", + //org.scala-lang.modules:scala-java8-compat_2.13: + // incompatible version change from 0.9.0 to 1.0.0 (compatibility: early semantic versioning) + "org.scala-lang.modules" %% "scala-java8-compat", + */ +) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 75c66fa..401f94e 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,19 +1,22 @@ -import sbt._ +import sbt.* object Dependencies { - val `config-tools` = "com.evolutiongaming" %% "config-tools" % "1.0.4" - val `future-helper` = "com.evolutiongaming" %% "future-helper" % "1.0.6" - val sequentially = "com.evolutiongaming" %% "sequentially" % "1.1.4" - val `akka-serialization` = "com.evolutiongaming" %% "akka-serialization" % "1.0.4" - val nel = "com.evolutiongaming" %% "nel" % "1.3.4" - val `safe-actor` = "com.evolutiongaming" %% "safe-actor" % "3.0.0" - val `scala-tools` = "com.evolutiongaming" %% "scala-tools" % "3.0.5" - val scalatest = "org.scalatest" %% "scalatest" % "3.2.14" + val `config-tools` = "com.evolutiongaming" %% "config-tools" % "1.0.5" + val `future-helper` = "com.evolutiongaming" %% "future-helper" % "1.0.7" + val sequentially = "com.evolutiongaming" %% "sequentially" % "1.2.0" + val `akka-serialization` = "com.evolutiongaming" %% "akka-serialization" % "1.1.0" + val nel = "com.evolutiongaming" %% "nel" % "1.3.5" + val `safe-actor` = "com.evolutiongaming" %% "safe-actor" % "3.1.0" + val `scala-tools` = "com.evolutiongaming" %% "scala-tools" % "3.0.6" + val scalatest = "org.scalatest" %% "scalatest" % "3.2.19" val `scala-logging` = "com.typesafe.scala-logging" %% "scala-logging" % "3.9.5" + val `scodec-bits` = "org.scodec" %% "scodec-bits" % "1.2.1" + val `scodec-core1` = "org.scodec" %% "scodec-core" % "1.11.10" + val `scodec-core2` = "org.scodec" %% "scodec-core" % "2.3.1" object Akka { - private val version = "2.6.19" + private val version = "2.6.21" val actor = "com.typesafe.akka" %% "akka-actor" % version val remote = "com.typesafe.akka" %% "akka-remote" % version val cluster = "com.typesafe.akka" %% "akka-cluster" % version @@ -23,12 +26,7 @@ object Dependencies { } object AkkaTools { - private val version = "3.0.12" + private val version = "3.0.13" val test = "com.evolutiongaming" %% "akka-tools-test" % version } - - object Scodec { - val core = "org.scodec" %% "scodec-core" % "1.11.3" - val bits = "org.scodec" %% "scodec-bits" % "1.1.9" - } } diff --git a/project/build.properties b/project/build.properties index c12f2b9..cb409aa 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.2 \ No newline at end of file +sbt.version=1.10.1 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 9a57b00..aabf983 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,11 @@ -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.6") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.2.0") -addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.2") +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.13") -addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") +addSbtPlugin("com.github.sbt" % "sbt-release" % "1.4.0") addSbtPlugin("com.evolution" % "sbt-scalac-opts-plugin" % "0.0.9") -addSbtPlugin("com.evolution" % "sbt-artifactory-plugin" % "0.0.2") \ No newline at end of file +addSbtPlugin("com.evolution" % "sbt-artifactory-plugin" % "0.0.2") + +addSbtPlugin("ch.epfl.scala" % "sbt-version-policy" % "3.2.1") \ No newline at end of file diff --git a/src/main/scala/com/evolutiongaming/concurrent/NamedDispatcher.scala b/src/main/scala/com/evolutiongaming/concurrent/NamedDispatcher.scala index b4829c2..6d758a0 100644 --- a/src/main/scala/com/evolutiongaming/concurrent/NamedDispatcher.scala +++ b/src/main/scala/com/evolutiongaming/concurrent/NamedDispatcher.scala @@ -4,8 +4,10 @@ import akka.actor.ActorSystem import scala.concurrent.ExecutionContext -final case class NamedDispatcher(name: String, implicit val ec: ExecutionContext) +@deprecated(message = "roll out your own NamedDispatcher, this one will be removed", since = "1.3.0") +final case class NamedDispatcher(name: String, ec: ExecutionContext) +@deprecated(message = "roll out your own NamedDispatcher, this one will be removed", since = "1.3.0") object NamedDispatcher { def apply(actorSystem: ActorSystem): NamedDispatcher = { diff --git a/src/main/scala/com/evolutiongaming/conhub/ConHub.scala b/src/main/scala/com/evolutiongaming/conhub/ConHub.scala index 5a99d64..6b52af5 100644 --- a/src/main/scala/com/evolutiongaming/conhub/ConHub.scala +++ b/src/main/scala/com/evolutiongaming/conhub/ConHub.scala @@ -27,7 +27,7 @@ object ConHub { def !(msg: M): SR /** - * @param msg messages will be delivered to all matched connections whether it is Local or Remote connection + * @param msgs messages will be delivered to all matched connections whether it is Local or Remote connection * @return list of local connections that matched the message */ def !(msgs: Nel[M]): SR @@ -44,7 +44,8 @@ object ConHub { def cons: Iterable[C] = conStates.values.values - def consLocal: Iterable[C.Local] = cons.collect { case x: C.Local => x } + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + def consLocal: Iterable[C.Local] = cons.collect { case x: C.Local@unchecked => x } def consRemote: Iterable[C.Remote] = cons.collect { case x: C.Remote => x } } diff --git a/src/main/scala/com/evolutiongaming/conhub/ConHubImpl.scala b/src/main/scala/com/evolutiongaming/conhub/ConHubImpl.scala index 9a7caeb..7fca302 100644 --- a/src/main/scala/com/evolutiongaming/conhub/ConHubImpl.scala +++ b/src/main/scala/com/evolutiongaming/conhub/ConHubImpl.scala @@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicBoolean import com.evolutiongaming.concurrent.sequentially.Sequentially import com.evolutiongaming.nel.Nel -import com.evolutiongaming.concurrent.FutureHelper._ +import com.evolutiongaming.concurrent.FutureHelper.* import com.typesafe.scalalogging.LazyLogging import scala.concurrent.duration.FiniteDuration @@ -43,7 +43,7 @@ object ConHubImpl extends LazyLogging { executor: ExecutionContext ): ConHub[Id, A, M, L] = { - implicit val executor1 = executor + implicit val executor1: ExecutionContext = executor new ConHub[Id, A, M, L] { diff --git a/src/main/scala/com/evolutiongaming/conhub/ConHubSerializer.scala b/src/main/scala/com/evolutiongaming/conhub/ConHubSerializer.scala index 8ce8e9d..d080750 100644 --- a/src/main/scala/com/evolutiongaming/conhub/ConHubSerializer.scala +++ b/src/main/scala/com/evolutiongaming/conhub/ConHubSerializer.scala @@ -3,15 +3,16 @@ package com.evolutiongaming.conhub import java.io.NotSerializableException import akka.serialization.SerializerWithStringManifest -import com.evolutiongaming.conhub.{RemoteEvent => R} +import com.evolutiongaming.conhub.RemoteEvent as R import com.evolutiongaming.nel.Nel import scodec.bits.{BitVector, ByteVector} import scodec.{Attempt, Codec, DecodeResult, codecs} -import scala.concurrent.duration._ +import scala.annotation.nowarn +import scala.concurrent.duration.* class ConHubSerializer extends SerializerWithStringManifest { - import ConHubSerializer._ + import ConHubSerializer.* private val EventManifest = "A" private val MsgsManifest = "C" @@ -43,6 +44,10 @@ class ConHubSerializer extends SerializerWithStringManifest { } } +//suppresses comp warning for 2.13 with -Xsource:3 +@nowarn( + "msg=Implicit method .+ was found in a package prefix of the required type, which is not part of the implicit scope in Scala 3" +) object ConHubSerializer { val codecBytes: Codec[ByteVector] = codecs.variableSizeBytes(codecs.int32, codecs.bytes) @@ -68,9 +73,9 @@ object ConHubSerializer { private val codecSync = codecsNel(codecValue).as[RemoteEvent.Event.Sync] - private def notSerializable(msg: String) = throw new NotSerializableException(msg) + private def notSerializable(msg: String): Nothing = throw new NotSerializableException(msg) - private def illegalArgument(msg: String) = throw new IllegalArgumentException(msg) + private def illegalArgument(msg: String): Nothing = throw new IllegalArgumentException(msg) private def eventFromBinary(bits: BitVector) = { diff --git a/src/main/scala/com/evolutiongaming/conhub/ConStates.scala b/src/main/scala/com/evolutiongaming/conhub/ConStates.scala index d0478f8..dc939c7 100644 --- a/src/main/scala/com/evolutiongaming/conhub/ConStates.scala +++ b/src/main/scala/com/evolutiongaming/conhub/ConStates.scala @@ -4,7 +4,7 @@ import java.time.Instant import akka.actor.{Address, Scheduler} import com.evolutiongaming.concurrent.sequentially.{MapDirective, SequentialMap} -import com.evolutiongaming.conhub.SequentialMapHelper._ +import com.evolutiongaming.conhub.SequentialMapHelper.* import com.typesafe.scalalogging.LazyLogging import scodec.bits.ByteVector @@ -70,7 +70,7 @@ object ConStates { private val send: SendEvent[Id, A] = connect(this) - def values = states.values + def values: collection.Map[Id, C] = states.values def update(id: Id, con: C.Local): Result = { updatePf(id, Some(con.version), "update") { case before => @@ -111,7 +111,8 @@ object ConStates { } (ctx, c) match { - case (Ctx.Local, _: C.Local) => disconnect(local = true) + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + case (Ctx.Local, _: C.Local@unchecked) => disconnect(local = true) case (ctx: Ctx.Remote, c: C.Remote) if c.address == ctx.address => disconnect(local = false) case _ => R.Ignore } @@ -130,7 +131,8 @@ object ConStates { def remove(local: Boolean) = this.remove(id, version, local) (ctx, c) match { - case (Ctx.Local, _: C.Local) => remove(local = true) + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + case (Ctx.Local, _: C.Local@unchecked) => remove(local = true) case (ctx: Ctx.Remote, c: C.Remote) if c.address == ctx.address => remove(local = false) case (_, _: C.Disconnected) => remove(local = ctx == Ctx.Local) case _ => R.Ignore @@ -139,7 +141,8 @@ object ConStates { } def sync(id: Id) = { - updatePf(id, None, "sync") { case Some(c: C.Local) => + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + updatePf(id, None, "sync") { case Some(c: C.Local@unchecked) => send.sync(id, c.value, c.version) R.Ignore } diff --git a/src/main/scala/com/evolutiongaming/conhub/MemberEventSubscribe.scala b/src/main/scala/com/evolutiongaming/conhub/MemberEventSubscribe.scala index 141db83..3b1577c 100644 --- a/src/main/scala/com/evolutiongaming/conhub/MemberEventSubscribe.scala +++ b/src/main/scala/com/evolutiongaming/conhub/MemberEventSubscribe.scala @@ -2,7 +2,7 @@ package com.evolutiongaming.conhub import akka.actor.{Actor, ActorRefFactory, Props} import akka.cluster.Cluster -import akka.cluster.ClusterEvent._ +import akka.cluster.ClusterEvent.* import com.evolutiongaming.safeakka.actor.ActorLog object MemberEventSubscribe { @@ -16,8 +16,9 @@ object MemberEventSubscribe { onEvent: MemberEvent => Unit): Unsubscribe = { def actor() = new Actor { - lazy val log = ActorLog(context.system, MemberEventSubscribe.getClass) - def receive = { + private lazy val log = ActorLog(context.system, classOf[MemberEventSubscribe.type]) + + def receive: Receive = { case x: CurrentClusterState => onState(x) case x: MemberEvent => onEvent(x) case x => log.warn(s"unexpected $x") diff --git a/src/main/scala/com/evolutiongaming/conhub/SendEvent.scala b/src/main/scala/com/evolutiongaming/conhub/SendEvent.scala index c500916..a41bdb2 100644 --- a/src/main/scala/com/evolutiongaming/conhub/SendEvent.scala +++ b/src/main/scala/com/evolutiongaming/conhub/SendEvent.scala @@ -2,7 +2,7 @@ package com.evolutiongaming.conhub import akka.actor.{ActorRefFactory, ActorSystem, Address} import com.evolutiongaming.conhub.transport.{ReceiveMsg, SendMsg} -import com.evolutiongaming.conhub.{RemoteEvent => R} +import com.evolutiongaming.conhub.RemoteEvent as R import com.evolutiongaming.nel.Nel import scala.concurrent.duration.FiniteDuration diff --git a/src/main/scala/com/evolutiongaming/conhub/SendMsgs.scala b/src/main/scala/com/evolutiongaming/conhub/SendMsgs.scala index cc39895..af72e2e 100644 --- a/src/main/scala/com/evolutiongaming/conhub/SendMsgs.scala +++ b/src/main/scala/com/evolutiongaming/conhub/SendMsgs.scala @@ -21,8 +21,9 @@ object SendMsgs { def apply(msg: M, con: C.Connected): Unit = { con match { - case con: C.Local => con.send(MsgAndRemote(msg)) - case con: C.Remote => remote(Nel(msg), List(con.address)) + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + case con: C.Local@unchecked => con.send(MsgAndRemote(msg)) + case con: C.Remote => remote(Nel(msg), List(con.address)) } } @@ -33,8 +34,9 @@ object SendMsgs { def local(msg: M, cons: Iterable[C], remote: Boolean): Unit = { val msgAndRemote = MsgAndRemote(msg, remote) for {con <- cons} con match { - case x: C.Local => x.send(msgAndRemote) - case _ => + //@unchecked needed to work around a Scala 3.3.3 compiler quirk with pattern matching + case x: C.Local@unchecked => x.send(msgAndRemote) + case _ => } } } diff --git a/src/main/scala/com/evolutiongaming/conhub/SequentialMapHelper.scala b/src/main/scala/com/evolutiongaming/conhub/SequentialMapHelper.scala index c9ce8a7..1e2fb11 100644 --- a/src/main/scala/com/evolutiongaming/conhub/SequentialMapHelper.scala +++ b/src/main/scala/com/evolutiongaming/conhub/SequentialMapHelper.scala @@ -1,9 +1,8 @@ package com.evolutiongaming.conhub -import com.evolutiongaming.concurrent.CurrentThreadExecutionContext import com.evolutiongaming.concurrent.sequentially.{MapDirective, SequentialMap} -import scala.concurrent.Future +import scala.concurrent.{ExecutionContext, Future} object SequentialMapHelper { @@ -25,7 +24,7 @@ object SequentialMapHelper { value: V, onUpdated: (K, Set[V], Set[V]) => Unit = (_, _, _) => ()): Future[Unit] = { - implicit val ec = CurrentThreadExecutionContext + implicit val ec: ExecutionContext = ExecutionContext.parasitic if (before != after) { val futureBefore = before.fold(Future.unit) { key => updateSet(key)(_ - value, onUpdated(key, _, _)) } @@ -59,6 +58,6 @@ object SequentialMapHelper { implicit class FutureOps[A](val self: Future[A]) extends AnyVal { // to execute f strictly in order of future origin - def mapNow[B](f: A => B): Future[B] = self.map(f)(CurrentThreadExecutionContext) + def mapNow[B](f: A => B): Future[B] = self.map(f)(ExecutionContext.parasitic) } } diff --git a/src/main/scala/com/evolutiongaming/conhub/UpdateResult.scala b/src/main/scala/com/evolutiongaming/conhub/UpdateResult.scala index 2542102..a8d0467 100644 --- a/src/main/scala/com/evolutiongaming/conhub/UpdateResult.scala +++ b/src/main/scala/com/evolutiongaming/conhub/UpdateResult.scala @@ -18,7 +18,10 @@ object UpdateResult { def created[A]: UpdateResult[A] = Created - def apply[A](updated: Boolean, value: A): UpdateResult[A] = UpdateResult(updated, Some(value)) + def apply[A](updated: Boolean, value: A): UpdateResult[A] = UpdateResult[A]( + updated = updated, + value = Some(value), + ) - def apply[A](value: A): UpdateResult[A] = UpdateResult(value = Some(value)) + def apply[A](value: A): UpdateResult[A] = UpdateResult[A](value = Some(value)) } \ No newline at end of file diff --git a/src/main/scala/com/evolutiongaming/conhub/transport/SendMsg.scala b/src/main/scala/com/evolutiongaming/conhub/transport/SendMsg.scala index 596c290..f9e27ca 100644 --- a/src/main/scala/com/evolutiongaming/conhub/transport/SendMsg.scala +++ b/src/main/scala/com/evolutiongaming/conhub/transport/SendMsg.scala @@ -1,11 +1,12 @@ package com.evolutiongaming.conhub.transport -import akka.actor._ -import akka.cluster.ClusterEvent._ +import akka.actor.* +import akka.cluster.ClusterEvent.* import akka.cluster.{Cluster, Member, MemberStatus} import com.evolutiongaming.safeakka.actor.ActorLog -import scala.concurrent.duration._ +import scala.concurrent.ExecutionContext +import scala.concurrent.duration.* import scala.reflect.ClassTag import scala.util.control.NonFatal @@ -43,12 +44,12 @@ object SendMsg { def validate(cluster: Cluster): Unit = if (!cluster.selfRoles.contains(role)) - sys.error(s"Current node doesn't contain conhub's role ${ role }") + sys.error(s"Current node doesn't contain conhub's role $role") if (system.hasExtension(Cluster)) { val cluster = Cluster(system) validate(cluster) - val log = ActorLog(system, classOf[SendMsg[_]]) prefixed name + val log = ActorLog(system, classOf[SendMsg[?]]) prefixed name apply(name, receive, factory, retryInterval, cluster, role, log) } else { empty @@ -85,7 +86,7 @@ object SendMsg { } - implicit val tell = new Tell[A] {} + implicit val tell: Tell[A] = new Tell[A] {} var state = Map.empty[Address, Channel] @@ -122,10 +123,10 @@ object SendMsg { } } - def actor() = new Actor { + def actor(): Actor = new Actor { - val scheduler = context.system.scheduler - implicit val ec = context.dispatcher + private val scheduler = context.system.scheduler + private implicit val ec: ExecutionContext = context.dispatcher def identify(address: Address, id: Long): Unit = { log.debug(s"identify $address $id") @@ -242,7 +243,7 @@ object SendMsg { disconnect(address) } - def receive = { + def receive: Receive = { case x: MemberEvent => onMemberEvent(x) case x: CurrentClusterState => onClusterState(x) case ActorIdentity(id: Long, ref) => onActorIdentity(id, ref) @@ -258,40 +259,35 @@ object SendMsg { val ref = factory.actorOf(props, name) cluster.subscribe(ref, classOf[MemberEvent]) - new SendMsg[A] { + (msg: A, addresses: Iterable[Address]) => { - def apply(msg: A, addresses: Iterable[Address]): Unit = { - - def broadcast() = { - log.debug(s"broadcast $msg") - for { - (address, channel) <- state - } channel match { - case channel: Channel.Connected => channel(msg) - case _ => ref.tell(address, msg) - } + def broadcast(): Unit = { + log.debug(s"broadcast $msg") + for { + (address, channel) <- state + } channel match { + case channel: Channel.Connected => channel(msg) + case _ => ref.tell(address, msg) } + } - def send() = { - log.debug(s"send $msg to ${ addresses mkString "," }") - for { - address <- addresses - } state.get(address) match { - case Some(channel: Channel.Connected) => channel(msg) - case _ => ref.tell(address, msg) - } + def send(): Unit = { + log.debug(s"send $msg to ${ addresses mkString "," }") + for { + address <- addresses + } state.get(address) match { + case Some(channel: Channel.Connected) => channel(msg) + case _ => ref.tell(address, msg) } - - if (addresses.isEmpty) broadcast() else send() } + + if (addresses.isEmpty) broadcast() else send() } } def apply[A, B](sendMsg: SendMsg[A], f: B => A): SendMsg[B] = { - new SendMsg[B] { - def apply(msg: B, addresses: Iterable[Address]): Unit = { - sendMsg(f(msg), addresses) - } + (msg: B, addresses: Iterable[Address]) => { + sendMsg(f(msg), addresses) } } diff --git a/src/test/scala/com/evolutiongaming/conhub/ConHubSerializerSpec.scala b/src/test/scala/com/evolutiongaming/conhub/ConHubSerializerSpec.scala index 7f5452a..ba47c2d 100644 --- a/src/test/scala/com/evolutiongaming/conhub/ConHubSerializerSpec.scala +++ b/src/test/scala/com/evolutiongaming/conhub/ConHubSerializerSpec.scala @@ -1,15 +1,15 @@ package com.evolutiongaming.conhub -import com.evolutiongaming.conhub.{RemoteEvent => R} +import com.evolutiongaming.conhub.RemoteEvent as R import com.evolutiongaming.nel.Nel import scodec.bits.ByteVector -import scala.concurrent.duration._ +import scala.concurrent.duration.* import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers class ConHubSerializerSpec extends AnyFunSuite with Matchers { - import ConHubSerializerSpec._ + import ConHubSerializerSpec.* private val serializer = new ConHubSerializer diff --git a/src/test/scala/com/evolutiongaming/conhub/ConHubSpec.scala b/src/test/scala/com/evolutiongaming/conhub/ConHubSpec.scala index 128c152..469e676 100644 --- a/src/test/scala/com/evolutiongaming/conhub/ConHubSpec.scala +++ b/src/test/scala/com/evolutiongaming/conhub/ConHubSpec.scala @@ -1,13 +1,12 @@ package com.evolutiongaming.conhub import akka.actor.Address -import com.evolutiongaming.concurrent.CurrentThreadExecutionContext import com.evolutiongaming.concurrent.sequentially.Sequentially -import com.evolutiongaming.concurrent.FutureHelper._ +import com.evolutiongaming.concurrent.FutureHelper.* import com.evolutiongaming.nel.Nel import scodec.bits.ByteVector -import scala.concurrent.Future +import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.duration.FiniteDuration import org.scalatest.wordspec.AnyWordSpec @@ -72,6 +71,6 @@ class ConHubSpec extends AnyWordSpec { MsgOps, EmptyConMetrics, connect, - CurrentThreadExecutionContext) + ExecutionContext.parasitic) } } \ No newline at end of file diff --git a/src/test/scala/com/evolutiongaming/conhub/ConHubSpecHelper.scala b/src/test/scala/com/evolutiongaming/conhub/ConHubSpecHelper.scala index 10a0c07..85a020f 100644 --- a/src/test/scala/com/evolutiongaming/conhub/ConHubSpecHelper.scala +++ b/src/test/scala/com/evolutiongaming/conhub/ConHubSpecHelper.scala @@ -9,9 +9,9 @@ trait ConHubSpecHelper extends ConnTypes[Connection, ConHubSpecHelper.Id] { val send: Send = new Send - val version = Version.Zero + val version: Version = Version.Zero - def newLocal(connection: Connection, send: Send = send) = { + def newLocal(connection: Connection, send: Send = send): Conn.Local[Connection, Msg] = { Conn.Local[Connection, Msg](connection, send, version) } } @@ -23,8 +23,8 @@ object ConHubSpecHelper { def newId(): String = UUID.randomUUID().toString object ConnectionSerializer extends Serializer.Bin[Connection] { - def to(x: Connection) = ByteVector.encodeUtf8(x.id).fold(throw _, identity) - def from(bytes: ByteVector) = Connection(bytes.decodeUtf8.fold(throw _, identity)) + def to(x: Connection): ByteVector = ByteVector.encodeUtf8(x.id).fold(throw _, identity) + def from(bytes: ByteVector): Connection = Connection(bytes.decodeUtf8.fold(throw _, identity)) } diff --git a/src/test/scala/com/evolutiongaming/conhub/ConStatesSpec.scala b/src/test/scala/com/evolutiongaming/conhub/ConStatesSpec.scala index d0c7b4f..090e771 100644 --- a/src/test/scala/com/evolutiongaming/conhub/ConStatesSpec.scala +++ b/src/test/scala/com/evolutiongaming/conhub/ConStatesSpec.scala @@ -4,16 +4,15 @@ import java.time.Instant import akka.actor.{ActorRef, Address} import akka.testkit.TestProbe -import com.evolutiongaming.concurrent.CurrentThreadExecutionContext import com.evolutiongaming.concurrent.sequentially.{SequentialMap, Sequentially} -import com.evolutiongaming.conhub.ConHubSpecHelper._ +import com.evolutiongaming.conhub.ConHubSpecHelper.* import com.evolutiongaming.conhub.ConStates.{Ctx, Diff} import com.evolutiongaming.conhub.transport.SendMsg -import com.evolutiongaming.conhub.{RemoteEvent => R} +import com.evolutiongaming.conhub.RemoteEvent as R import com.evolutiongaming.test.ActorSpec -import scala.concurrent.Future -import scala.concurrent.duration._ +import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.duration.* import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec @@ -185,7 +184,6 @@ class ConStatesSpec extends AnyWordSpec with ActorSpec with Matchers with ConHub val remote = Conn.Remote(connection, address, version) val pubSubProbe = TestProbe() - val listenerProbe = TestProbe() val states = SequentialMap[Id, Conn[Connection, Msg]](Sequentially.now) @@ -203,9 +201,7 @@ class ConStatesSpec extends AnyWordSpec with ActorSpec with Matchers with ConHub ConnectionSerializer, onStateChanged, () => instant, - connect)(CurrentThreadExecutionContext) - - val send = new Send + connect)(ExecutionContext.parasitic) def onStateChanged(diff: Diff[Id, C]) = { testActor ! diff diff --git a/src/test/scala/com/evolutiongaming/conhub/SendMsgSpec.scala b/src/test/scala/com/evolutiongaming/conhub/SendMsgSpec.scala index d5638ae..dbd483f 100644 --- a/src/test/scala/com/evolutiongaming/conhub/SendMsgSpec.scala +++ b/src/test/scala/com/evolutiongaming/conhub/SendMsgSpec.scala @@ -13,7 +13,7 @@ class SendMsgSpec extends AnyWordSpec with ActorSpec with Matchers { "do not allow create SendMsg without conhub's role" in { val role = "dummy" val caught = intercept[RuntimeException] { - SendMsg("", ReceiveMsg.empty, system, role) + SendMsg[Unit]("", ReceiveMsg.empty, system, role) } caught.getMessage shouldEqual s"Current node doesn't contain conhub's role ${ role }" } diff --git a/version.sbt b/version.sbt index ad5dc0d..dc4c4e4 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -ThisBuild / version := "1.2.2-SNAPSHOT" +ThisBuild / version := "1.3.0-SNAPSHOT"