Skip to content

Commit

Permalink
Update deps, add Scala 3 support, drop Scala 2.12
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
migesok committed Sep 5, 2024
1 parent e099e53 commit 5cc297d
Show file tree
Hide file tree
Showing 22 changed files with 165 additions and 116 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ jobs:
strategy:
matrix:
scala:
- 2.13.8
- 2.12.17
- 2.13.14
- 3.3.3

steps:
- uses: actions/checkout@v3

- 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()
Expand Down
52 changes: 47 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Dependencies._
import Dependencies.*

name := "conhub"

Expand All @@ -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,
Expand All @@ -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
Expand All @@ -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",
*/
)
30 changes: 14 additions & 16 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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"
}
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.7.2
sbt.version=1.10.1
10 changes: 6 additions & 4 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -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")
addSbtPlugin("com.evolution" % "sbt-artifactory-plugin" % "0.0.2")

addSbtPlugin("ch.epfl.scala" % "sbt-version-policy" % "3.2.1")
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
5 changes: 3 additions & 2 deletions src/main/scala/com/evolutiongaming/conhub/ConHub.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/com/evolutiongaming/conhub/ConHubImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] {

Expand Down
15 changes: 10 additions & 5 deletions src/main/scala/com/evolutiongaming/conhub/ConHubSerializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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) = {
Expand Down
13 changes: 8 additions & 5 deletions src/main/scala/com/evolutiongaming/conhub/ConStates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 =>
Expand Down Expand Up @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/com/evolutiongaming/conhub/SendEvent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions src/main/scala/com/evolutiongaming/conhub/SendMsgs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand All @@ -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 _ =>
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {

Expand All @@ -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, _, _)) }
Expand Down Expand Up @@ -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)
}
}
Loading

0 comments on commit 5cc297d

Please sign in to comment.