Skip to content

Commit 3c98e90

Browse files
Merge pull request #278 from Kazark/ce3
feat: upgrade to Cats Effect 3
2 parents 9b9adfa + 211a94d commit 3c98e90

File tree

10 files changed

+163
-200
lines changed

10 files changed

+163
-200
lines changed

build.sbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ lazy val reload = project.in(file("modules/reload"))
7878
)
7979

8080
val catsV = "2.6.1"
81-
val catsEffectV = "2.5.1"
81+
val catsEffectV = "3.1.0"
8282
val catsCollectionV = "0.9.2"
8383

8484
val munitV = "0.7.25"
@@ -96,12 +96,12 @@ lazy val commonSettings = Seq(
9696
libraryDependencies ++= Seq(
9797
"org.typelevel" %% "cats-core" % catsV,
9898
"org.typelevel" %% "cats-effect" % catsEffectV,
99-
"io.chrisdavenport" %% "mapref" % "0.1.1",
99+
"io.chrisdavenport" %% "mapref" % "0.2.0-M1",
100100

101101
"org.typelevel" %% "cats-effect-laws" % catsEffectV % Test,
102102
"org.scalameta" %% "munit" % munitV % Test,
103103
"org.scalameta" %% "munit-scalacheck" % munitV % Test,
104-
"org.typelevel" %% "munit-cats-effect-2" % munitCEV % Test,
104+
"org.typelevel" %% "munit-cats-effect-3" % munitCEV % Test,
105105
)
106106
)
107107

modules/bench/src/main/scala/io/chrisdavenport/mules/LookupBench.scala

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.openjdk.jmh.annotations._
55

66
import cats.implicits._
77
import cats.effect._
8+
import cats.effect.unsafe.IORuntime
89
import io.chrisdavenport.mules.caffeine.CaffeineCache
910

1011

@@ -14,37 +15,37 @@ class LookUpBench {
1415
import LookUpBench._
1516

1617
@Benchmark
17-
def contentionSingleImmutableMap(in: BenchStateRef) =
18-
testUnderContention(in.memoryCache, in.readList, in.writeList)(in.CS)
18+
def contentionSingleImmutableMap(in: BenchStateRef) =
19+
testUnderContention(in.memoryCache, in.readList, in.writeList)(in.R)
1920

2021
@Benchmark
2122
def contentionConcurrentHashMap(in: BenchStateCHM) =
22-
testUnderContention(in.memoryCache, in.readList, in.writeList)(in.CS)
23+
testUnderContention(in.memoryCache, in.readList, in.writeList)(in.R)
2324

2425
@Benchmark
2526
def contentionCaffeine(in: BenchStateCaffeine) =
26-
testUnderContention(in.cache, in.readList, in.writeList)(in.CS)
27+
testUnderContention(in.cache, in.readList, in.writeList)(in.R)
2728

28-
def testUnderContention(m: Cache[IO, Int, String], r: List[Int], w: List[Int])(implicit CS: ContextShift[IO]) = {
29+
def testUnderContention(m: Cache[IO, Int, String], r: List[Int], w: List[Int])(implicit R: IORuntime) = {
2930
val set = w.traverse( m.insert(_, "foo"))
3031
val read = r.traverse(m.lookup(_))
3132
val action = (set, read).parMapN((_, _) => ())
3233
action.unsafeRunSync()
3334
}
3435

3536
@Benchmark
36-
def contentionReadsSingleImmutableMap(in: BenchStateRef) =
37-
underContentionWaitReads(in.memoryCache, in.readList, in.writeList)(in.CS)
37+
def contentionReadsSingleImmutableMap(in: BenchStateRef) =
38+
underContentionWaitReads(in.memoryCache, in.readList, in.writeList)(in.R)
3839

3940
@Benchmark
40-
def contentionReadsConcurrentHashMap(in: BenchStateCHM) =
41-
underContentionWaitReads(in.memoryCache, in.readList, in.writeList)(in.CS)
41+
def contentionReadsConcurrentHashMap(in: BenchStateCHM) =
42+
underContentionWaitReads(in.memoryCache, in.readList, in.writeList)(in.R)
4243

4344
@Benchmark
4445
def contentionReadsCaffeine(in: BenchStateCaffeine) =
45-
underContentionWaitReads(in.cache, in.readList, in.writeList)(in.CS)
46+
underContentionWaitReads(in.cache, in.readList, in.writeList)(in.R)
4647

47-
def underContentionWaitReads(m: Cache[IO, Int, String], r: List[Int], w: List[Int])(implicit CS: ContextShift[IO]) = {
48+
def underContentionWaitReads(m: Cache[IO, Int, String], r: List[Int], w: List[Int])(implicit R: IORuntime) = {
4849
val set = w.traverse(m.insert(_, "foo"))
4950
val read = r.traverse(m.lookup(_))
5051
Concurrent[IO].bracket(set.start)(
@@ -53,18 +54,18 @@ class LookUpBench {
5354
}
5455

5556
@Benchmark
56-
def contentionWritesSingleImmutableMap(in: BenchStateRef) =
57-
underContentionWaitWrites(in.memoryCache, in.readList, in.writeList)(in.CS)
57+
def contentionWritesSingleImmutableMap(in: BenchStateRef) =
58+
underContentionWaitWrites(in.memoryCache, in.readList, in.writeList)(in.R)
5859

5960
@Benchmark
60-
def contentionWritesConcurrentHashMap(in: BenchStateCHM) =
61-
underContentionWaitWrites(in.memoryCache, in.readList, in.writeList)(in.CS)
61+
def contentionWritesConcurrentHashMap(in: BenchStateCHM) =
62+
underContentionWaitWrites(in.memoryCache, in.readList, in.writeList)(in.R)
6263

6364
@Benchmark
64-
def contentionWritesCaffeine(in: BenchStateCaffeine) =
65-
underContentionWaitWrites(in.cache, in.readList, in.writeList)(in.CS)
65+
def contentionWritesCaffeine(in: BenchStateCaffeine) =
66+
underContentionWaitWrites(in.cache, in.readList, in.writeList)(in.R)
6667

67-
def underContentionWaitWrites(m: Cache[IO, Int, String],r: List[Int], w: List[Int])(implicit CS: ContextShift[IO]) = {
68+
def underContentionWaitWrites(m: Cache[IO, Int, String],r: List[Int], w: List[Int])(implicit R: IORuntime) = {
6869
val set = w.traverse( m.insert(_, "foo"))
6970
val read = r.traverse(m.lookup(_))
7071
Concurrent[IO].bracket(read.start)(
@@ -80,8 +81,7 @@ object LookUpBench {
8081
var memoryCache: MemoryCache[IO, Int, String] = _
8182
val writeList: List[Int] = (1 to 100).toList
8283
val readList : List[Int] = (1 to 100).toList
83-
implicit val T = IO.timer(scala.concurrent.ExecutionContext.global)
84-
implicit val CS = IO.contextShift(scala.concurrent.ExecutionContext.global)
84+
implicit val R = IORuntime.global
8585

8686
@Setup(Level.Trial)
8787
def setup(): Unit = {
@@ -94,8 +94,7 @@ object LookUpBench {
9494
var memoryCache: MemoryCache[IO, Int, String] = _
9595
val writeList: List[Int] = (1 to 100).toList
9696
val readList : List[Int] = (1 to 100).toList
97-
implicit val T = IO.timer(scala.concurrent.ExecutionContext.global)
98-
implicit val CS = IO.contextShift(scala.concurrent.ExecutionContext.global)
97+
implicit val R = IORuntime.global
9998

10099
@Setup(Level.Trial)
101100
def setup(): Unit = {
@@ -110,13 +109,12 @@ object LookUpBench {
110109
var cache: Cache[IO, Int, String] = _
111110
val writeList: List[Int] = (1 to 100).toList
112111
val readList : List[Int] = (1 to 100).toList
113-
implicit val T = IO.timer(scala.concurrent.ExecutionContext.global)
114-
implicit val CS = IO.contextShift(scala.concurrent.ExecutionContext.global)
112+
implicit val R = IORuntime.global
115113

116114
@Setup(Level.Trial)
117115
def setup(): Unit = {
118116
cache = CaffeineCache.build[IO, Int, String](None, None, None).unsafeRunSync()
119117
cache.insert(1, "yellow").unsafeRunSync()
120118
}
121119
}
122-
}
120+
}

modules/caffeine/src/test/scala/io/chrisdavenport/mules/caffeine/CaffeineCacheSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class CaffeineCacheSpec extends CatsEffectSuite {
1010
for {
1111
cache <- CaffeineCache.build[IO, String, Int](Some(TimeSpec.unsafeFromDuration(1.second)), None, None)
1212
_ <- cache.insert("Foo", 1)
13-
_ <- Timer[IO].sleep(1.milli)
13+
_ <- Temporal[IO].sleep(1.milli)
1414
value <- cache.lookup("Foo")
1515
} yield {
1616
assertEquals(value, Some(1))
@@ -34,7 +34,7 @@ class CaffeineCacheSpec extends CatsEffectSuite {
3434
for {
3535
cache <- CaffeineCache.build[IO, String, Int](Some(TimeSpec.unsafeFromDuration(1.second)), None, None)
3636
_ <- cache.insert("Foo", 1)
37-
_ <- Timer[IO].sleep(2.second)
37+
_ <- Temporal[IO].sleep(2.second)
3838
value <- cache.lookup("Foo")
3939
} yield {
4040
assertEquals(value, None)

modules/core/src/main/scala/io/chrisdavenport/mules/DispatchOneCache.scala

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package io.chrisdavenport.mules
22

33
import cats.effect._
4-
import cats.effect.concurrent._
54
import cats.effect.implicits._
65
import cats.implicits._
7-
import scala.concurrent.duration._
86
import scala.collection.immutable.Map
97

108
import io.chrisdavenport.mapref.MapRef
@@ -37,19 +35,19 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
3735
private val purgeExpiredEntries: Long => F[List[K]] =
3836
purgeExpiredEntriesOpt.getOrElse(purgeExpiredEntriesDefault)
3937

40-
private val emptyFV = F.pure(Option.empty[TryableDeferred[F, Either[Throwable, V]]])
38+
private val emptyFV = F.pure(Option.empty[Deferred[F, Either[Throwable, V]]])
4139

42-
private val createEmptyIfUnset: K => F[Option[TryableDeferred[F, Either[Throwable, V]]]] =
43-
k => Deferred.tryable[F, Either[Throwable, V]].flatMap{deferred =>
44-
C.monotonic(NANOSECONDS).flatMap{ now =>
45-
val timeout = defaultExpiration.map(ts => TimeSpec.unsafeFromNanos(now + ts.nanos))
40+
private val createEmptyIfUnset: K => F[Option[Deferred[F, Either[Throwable, V]]]] =
41+
k => Deferred[F, Either[Throwable, V]].flatMap{deferred =>
42+
C.monotonic.flatMap{ now =>
43+
val timeout = defaultExpiration.map(ts => TimeSpec.unsafeFromNanos(now.toNanos + ts.nanos))
4644
mapRef(k).modify{
4745
case None => (DispatchOneCacheItem[F, V](deferred, timeout).some, deferred.some)
4846
case s@Some(_) => (s, None)
4947
}}
5048
}
5149

52-
private val updateIfFailedThenCreate: (K, DispatchOneCacheItem[F, V]) => F[Option[TryableDeferred[F, Either[Throwable, V]]]] =
50+
private val updateIfFailedThenCreate: (K, DispatchOneCacheItem[F, V]) => F[Option[Deferred[F, Either[Throwable, V]]]] =
5351
(k, cacheItem) => cacheItem.item.tryGet.flatMap{
5452
case Some(Left(_)) =>
5553
mapRef(k).modify{
@@ -72,8 +70,8 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
7270
maybeDeferred.bracketCase(_.traverse_{ deferred =>
7371
action(k).attempt.flatMap(e => deferred.complete(e).attempt.void)
7472
}){
75-
case (Some(deferred), ExitCase.Canceled) => deferred.complete(CancelationDuringDispatchOneCacheInsertProcessing.asLeft).attempt.void
76-
case (Some(deferred), ExitCase.Error(e)) => deferred.complete(e.asLeft).attempt.void
73+
case (Some(deferred), Outcome.Canceled()) => deferred.complete(CancelationDuringDispatchOneCacheInsertProcessing.asLeft).attempt.void
74+
case (Some(deferred), Outcome.Errored(e)) => deferred.complete(e.asLeft).attempt.void
7775
case _ => F.unit
7876
}
7977
}
@@ -84,11 +82,11 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
8482
* gets the value in the system
8583
**/
8684
def lookupOrLoad(k: K, action: K => F[V]): F[V] = {
87-
C.monotonic(NANOSECONDS)
85+
C.monotonic
8886
.flatMap{now =>
8987
mapRef(k).modify[Option[DispatchOneCacheItem[F, V]]]{
9088
case s@Some(value) =>
91-
if (DispatchOneCache.isExpired(now, value)){
89+
if (DispatchOneCache.isExpired(now.toNanos, value)){
9290
(None, None)
9391
} else {
9492
(s, s)
@@ -108,22 +106,22 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
108106

109107
def insertWith(k: K, action: K => F[V]): F[Unit] = {
110108
for {
111-
defer <- Deferred.tryable[F, Either[Throwable, V]]
112-
now <- Clock[F].monotonic(NANOSECONDS)
113-
item = DispatchOneCacheItem(defer, defaultExpiration.map(spec => TimeSpec.unsafeFromNanos(now + spec.nanos))).some
109+
defer <- Deferred[F, Either[Throwable, V]]
110+
now <- Clock[F].monotonic
111+
item = DispatchOneCacheItem(defer, defaultExpiration.map(spec => TimeSpec.unsafeFromNanos(now.toNanos + spec.nanos))).some
114112
out <- mapRef(k).getAndSet(item)
115113
.bracketCase{oldDeferOpt =>
116114
action(k).flatMap[Unit]{ a =>
117115
val set = a.asRight
118116
oldDeferOpt.traverse_(oldDefer => oldDefer.item.complete(set)).attempt >>
119-
defer.complete(set)
117+
defer.complete(set).void
120118
}
121119
}{
122-
case (_, ExitCase.Completed) => F.unit
123-
case (oldItem, ExitCase.Canceled) =>
120+
case (_, Outcome.Succeeded(_)) => F.unit
121+
case (oldItem, Outcome.Canceled()) =>
124122
val set = CancelationDuringDispatchOneCacheInsertProcessing.asLeft
125123
oldItem.traverse_(_.item.complete(set)).attempt >> defer.complete(set).attempt.void
126-
case (oldItem, ExitCase.Error(e)) =>
124+
case (oldItem, Outcome.Errored(e)) =>
127125
val set = e.asLeft
128126
oldItem.traverse_(_.item.complete(set)).attempt >> defer.complete(set).attempt.void
129127
}
@@ -134,11 +132,11 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
134132
* Overrides any background insert
135133
**/
136134
def insert(k: K, v: V): F[Unit] = for {
137-
defered <- Deferred.tryable[F, Either[Throwable, V]]
135+
defered <- Deferred[F, Either[Throwable, V]]
138136
setAs = v.asRight
139137
_ <- defered.complete(setAs)
140-
now <- C.monotonic(NANOSECONDS)
141-
item = DispatchOneCacheItem(defered, defaultExpiration.map(spec => TimeSpec.unsafeFromNanos(now + spec.nanos))).some
138+
now <- C.monotonic
139+
item = DispatchOneCacheItem(defered, defaultExpiration.map(spec => TimeSpec.unsafeFromNanos(now.toNanos + spec.nanos))).some
142140
action <- mapRef(k).modify{
143141
case None =>
144142
(item, F.unit)
@@ -153,11 +151,11 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
153151
* Overrides any background insert
154152
**/
155153
def insertWithTimeout(optionTimeout: Option[TimeSpec])(k: K, v: V): F[Unit] = for {
156-
defered <- Deferred.tryable[F, Either[Throwable, V]]
154+
defered <- Deferred[F, Either[Throwable, V]]
157155
setAs = v.asRight
158156
_ <- defered.complete(setAs)
159-
now <- C.monotonic(NANOSECONDS)
160-
item = DispatchOneCacheItem(defered, optionTimeout.map(spec => TimeSpec.unsafeFromNanos(now + spec.nanos))).some
157+
now <- C.monotonic
158+
item = DispatchOneCacheItem(defered, optionTimeout.map(spec => TimeSpec.unsafeFromNanos(now.toNanos + spec.nanos))).some
161159
action <- mapRef(k).modify{
162160
case None =>
163161
(item, F.unit)
@@ -168,11 +166,11 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
168166
} yield out
169167

170168
def lookup(k: K): F[Option[V]] = {
171-
C.monotonic(NANOSECONDS)
169+
C.monotonic
172170
.flatMap{now =>
173171
mapRef(k).modify[Option[DispatchOneCacheItem[F, V]]]{
174172
case s@Some(value) =>
175-
if (DispatchOneCache.isExpired(now, value)){
173+
if (DispatchOneCache.isExpired(now.toNanos, value)){
176174
(None, None)
177175
} else {
178176
(s, s)
@@ -208,16 +206,16 @@ final class DispatchOneCache[F[_], K, V] private[DispatchOneCache] (
208206
**/
209207
def purgeExpired: F[Unit] = {
210208
for {
211-
now <- C.monotonic(NANOSECONDS)
212-
_ <- purgeExpiredEntries(now)
209+
now <- C.monotonic
210+
_ <- purgeExpiredEntries(now.toNanos)
213211
} yield ()
214212
}
215213

216214
}
217215

218216
object DispatchOneCache {
219217
private case class DispatchOneCacheItem[F[_], A](
220-
item: TryableDeferred[F, Either[Throwable, A]],
218+
item: Deferred[F, Either[Throwable, A]],
221219
itemExpiration: Option[TimeSpec]
222220
)
223221
private case object CancelationDuringDispatchOneCacheInsertProcessing extends scala.util.control.NoStackTrace
@@ -231,13 +229,13 @@ object DispatchOneCache {
231229
*
232230
* @return an `Resource[F, Unit]` that will keep removing expired entries in the background.
233231
**/
234-
def liftToAuto[F[_]: Concurrent: Timer, K, V](
232+
def liftToAuto[F[_]: Temporal, K, V](
235233
DispatchOneCache: DispatchOneCache[F, K, V],
236234
checkOnExpirationsEvery: TimeSpec
237235
): Resource[F, Unit] = {
238236
def runExpiration(cache: DispatchOneCache[F, K, V]): F[Unit] = {
239237
val check = TimeSpec.toDuration(checkOnExpirationsEvery)
240-
Timer[F].sleep(check) >> cache.purgeExpired >> runExpiration(cache)
238+
Temporal[F].sleep(check) >> cache.purgeExpired >> runExpiration(cache)
241239
}
242240

243241
Resource.make(runExpiration(DispatchOneCache).start)(_.cancel).void
@@ -248,7 +246,7 @@ object DispatchOneCache {
248246
*
249247
* If the specified default expiration value is None, items inserted by insert will never expire.
250248
**/
251-
def ofSingleImmutableMap[F[_]: Concurrent: Clock, K, V](
249+
def ofSingleImmutableMap[F[_]: Async, K, V](
252250
defaultExpiration: Option[TimeSpec]
253251
): F[DispatchOneCache[F, K, V]] =
254252
Ref.of[F, Map[K, DispatchOneCacheItem[F, V]]](Map.empty[K, DispatchOneCacheItem[F, V]])
@@ -258,7 +256,7 @@ object DispatchOneCache {
258256
defaultExpiration
259257
))
260258

261-
def ofShardedImmutableMap[F[_]: Concurrent : Clock, K, V](
259+
def ofShardedImmutableMap[F[_]: Async, K, V](
262260
shardCount: Int,
263261
defaultExpiration: Option[TimeSpec]
264262
): F[DispatchOneCache[F, K, V]] =
@@ -270,7 +268,7 @@ object DispatchOneCache {
270268
)
271269
}
272270

273-
def ofConcurrentHashMap[F[_]: Concurrent: Clock, K, V](
271+
def ofConcurrentHashMap[F[_]: Async, K, V](
274272
defaultExpiration: Option[TimeSpec],
275273
initialCapacity: Int = 16,
276274
loadFactor: Float = 0.75f,

0 commit comments

Comments
 (0)