Skip to content

Commit 7fdf424

Browse files
authored
Merge pull request #345 from kubukoz/cats-effect
Add cats-effect to the core module, remove modes
2 parents a4977d0 + d1c29ad commit 7fdf424

File tree

49 files changed

+724
-1235
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+724
-1235
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
sudo: false
22
language: scala
33
scala:
4-
- 2.11.12
54
- 2.13.1
65
- 2.12.10
76
jdk:

build.sbt

+6-45
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ lazy val root: Project = Project(id = "scalacache", base = file("."))
2929
memcached,
3030
redis,
3131
caffeine,
32-
catsEffect,
33-
scalaz72,
3432
circe,
3533
tests
3634
)
@@ -42,6 +40,7 @@ lazy val core =
4240
moduleName := "scalacache-core",
4341
libraryDependencies ++= Seq(
4442
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
43+
"org.typelevel" %%% "cats-effect" % "2.1.3",
4544
"org.scalatest" %%% "scalatest" % "3.0.8" % Test,
4645
"org.scalacheck" %%% "scalacheck" % "1.14.3" % Test
4746
),
@@ -51,10 +50,6 @@ lazy val core =
5150
.jvmSettings(
5251
libraryDependencies ++= Seq(
5352
"org.slf4j" % "slf4j-api" % "1.7.30"
54-
),
55-
scala211OnlyDeps(
56-
"org.squeryl" %% "squeryl" % "0.9.15" % Test,
57-
"com.h2database" % "h2" % "1.4.200" % Test
5853
)
5954
)
6055

@@ -96,38 +91,12 @@ lazy val caffeine = jvmOnlyModule("caffeine")
9691
coverageFailOnMinimum := true
9792
)
9893

99-
lazy val catsEffect = jvmOnlyModule("cats-effect")
100-
.settings(
101-
libraryDependencies ++= Seq(
102-
"org.typelevel" %% "cats-effect" % "2.0.0"
103-
),
104-
coverageMinimum := 50,
105-
coverageFailOnMinimum := true
106-
)
107-
108-
lazy val scalaz72 = jvmOnlyModule("scalaz72")
109-
.settings(
110-
libraryDependencies ++= Seq(
111-
"org.scalaz" %% "scalaz-concurrent" % "7.2.30"
112-
),
113-
coverageMinimum := 40,
114-
coverageFailOnMinimum := true
115-
)
116-
117-
def circeVersion(scalaVersion: String) =
118-
CrossVersion.partialVersion(scalaVersion) match {
119-
case Some((2, scalaMajor)) if scalaMajor >= 12 => "0.13.0"
120-
case Some((2, scalaMajor)) if scalaMajor >= 11 => "0.11.1"
121-
case _ =>
122-
throw new IllegalArgumentException(s"Unsupported Scala version $scalaVersion")
123-
}
124-
12594
lazy val circe = jvmOnlyModule("circe")
12695
.settings(
12796
libraryDependencies ++= Seq(
128-
"io.circe" %% "circe-core" % circeVersion(scalaVersion.value),
129-
"io.circe" %% "circe-parser" % circeVersion(scalaVersion.value),
130-
"io.circe" %% "circe-generic" % circeVersion(scalaVersion.value) % Test,
97+
"io.circe" %% "circe-core" % "0.13.0",
98+
"io.circe" %% "circe-parser" % "0.13.0",
99+
"io.circe" %% "circe-generic" % "0.13.0" % Test,
131100
scalacheck
132101
),
133102
coverageMinimum := 80,
@@ -136,7 +105,7 @@ lazy val circe = jvmOnlyModule("circe")
136105

137106
lazy val tests = jvmOnlyModule("tests")
138107
.settings(publishArtifact := false)
139-
.dependsOn(caffeine, memcached, redis, catsEffect, scalaz72, circe)
108+
.dependsOn(caffeine, memcached, redis, circe)
140109

141110
lazy val docs = jvmOnlyModule("docs")
142111
.enablePlugins(MicrositesPlugin)
@@ -160,8 +129,6 @@ lazy val docs = jvmOnlyModule("docs")
160129
memcached,
161130
redis,
162131
caffeine,
163-
catsEffect,
164-
scalaz72,
165132
circe
166133
)
167134

@@ -192,7 +159,7 @@ lazy val commonSettings =
192159
mavenSettings ++
193160
Seq(
194161
organization := "com.github.cb372",
195-
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"),
162+
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature", "-language:higherKinds"),
196163
parallelExecution in Test := false
197164
)
198165

@@ -202,9 +169,3 @@ lazy val mavenSettings = Seq(
202169
false
203170
}
204171
)
205-
206-
def scala211OnlyDeps(moduleIDs: ModuleID*) =
207-
libraryDependencies ++= (scalaBinaryVersion.value match {
208-
case "2.11" => moduleIDs
209-
case other => Nil
210-
})

modules/benchmarks/src/main/scala/scalacache/benchmark/CaffeineBenchmark.scala

+13-9
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,28 @@ import com.github.benmanes.caffeine.cache.Caffeine
99
import scalacache._
1010
import caffeine._
1111
import memoization._
12-
import scalacache.modes.sync._
12+
import cats.effect.SyncIO
13+
import cats.effect.Clock
1314

1415
@State(Scope.Thread)
1516
class CaffeineBenchmark {
1617

17-
val underlyingCache = Caffeine.newBuilder().build[String, Entry[String]]()
18-
implicit val cache: Cache[String] = CaffeineCache(underlyingCache)
18+
implicit val clockSyncIO = Clock.create[SyncIO]
1919

20-
val key = "key"
20+
val underlyingCache = Caffeine.newBuilder().build[String, Entry[String]]()
21+
implicit val cache: Cache[SyncIO, String] = CaffeineCache[SyncIO, String](underlyingCache)
22+
23+
val key = "key"
2124
val value: String = "value"
2225

23-
def itemCachedNoMemoize(key: String): Id[Option[String]] = {
24-
cache.get(key)
26+
def itemCachedNoMemoize(key: String): Option[String] = {
27+
cache.get(key).unsafeRunSync()
2528
}
2629

27-
def itemCachedMemoize(key: String): String = memoizeSync(None) {
28-
value
29-
}
30+
def itemCachedMemoize(key: String): String =
31+
memoize(None) {
32+
value
33+
}.unsafeRunSync()
3034

3135
// populate the cache
3236
cache.put(key)(value)

modules/benchmarks/src/test/scala/scalacache/benchmark/ProfilingMemoize.scala

+11-8
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,28 @@ import com.github.benmanes.caffeine.cache.Caffeine
55
import scalacache._
66
import scalacache.caffeine._
77
import scalacache.memoization._
8-
import scalacache.modes.sync._
8+
import cats.effect.SyncIO
9+
import cats.effect.Clock
910

1011
/**
1112
* Just runs forever, endlessly calling memoize, so Java Flight Recorder can output sampling data.
1213
*/
1314
object ProfilingMemoize extends App {
1415

15-
val underlyingCache = Caffeine.newBuilder().build[String, Entry[String]]()
16-
implicit val cache = CaffeineCache[String](underlyingCache)
16+
implicit val clockSyncIO = Clock.create[SyncIO]
17+
val underlyingCache = Caffeine.newBuilder().build[String, Entry[String]]()
18+
implicit val cache = CaffeineCache[SyncIO, String](underlyingCache)
1719

18-
val key = "key"
20+
val key = "key"
1921
val value: String = "value"
2022

21-
def itemCachedMemoize(key: String): String = memoizeSync(None) {
22-
value
23-
}
23+
def itemCachedMemoize(key: String): String =
24+
memoize(None) {
25+
value
26+
}.unsafeRunSync()
2427

2528
var result: String = _
26-
var i = 0L
29+
var i = 0L
2730

2831
while (i < Long.MaxValue) {
2932
result = itemCachedMemoize(key)
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,83 @@
11
package scalacache.caffeine
22

33
import java.time.temporal.ChronoUnit
4-
import java.time.{Clock, Instant}
4+
import java.time.{Instant}
55

66
import com.github.benmanes.caffeine.cache.{Caffeine, Cache => CCache}
7-
7+
import cats.effect.Clock
88
import scalacache.logging.Logger
9-
import scalacache.{AbstractCache, CacheConfig, Entry, Mode}
9+
import scalacache.{AbstractCache, CacheConfig, Entry}
1010
import scala.concurrent.duration.Duration
1111
import scala.language.higherKinds
12+
import cats.effect.Sync
13+
import java.util.concurrent.TimeUnit
14+
import cats.implicits._
15+
import cats.MonadError
1216

1317
/*
1418
* Thin wrapper around Caffeine.
1519
*
1620
* This cache implementation is synchronous.
1721
*/
18-
class CaffeineCache[V](val underlying: CCache[String, Entry[V]])(
22+
class CaffeineCache[F[_]: Sync, V](val underlying: CCache[String, Entry[V]])(
1923
implicit val config: CacheConfig,
20-
clock: Clock = Clock.systemUTC()
21-
) extends AbstractCache[V] {
24+
clock: Clock[F]
25+
) extends AbstractCache[F, V] {
26+
protected val F: Sync[F] = Sync[F]
2227

2328
override protected final val logger = Logger.getLogger(getClass.getName)
2429

25-
def doGet[F[_]](key: String)(implicit mode: Mode[F]): F[Option[V]] = {
26-
mode.M.delay {
27-
val entry = underlying.getIfPresent(key)
28-
val result = {
29-
if (entry == null || entry.isExpired)
30-
None
31-
else
32-
Some(entry.value)
30+
def doGet(key: String): F[Option[V]] = {
31+
F.delay {
32+
Option(underlying.getIfPresent(key))
33+
}
34+
.flatMap(_.filterA(Entry.isExpired[F, V]))
35+
.map(_.map(_.value))
36+
.flatTap { result =>
37+
logCacheHitOrMiss(key, result)
3338
}
34-
logCacheHitOrMiss(key, result)
35-
result
36-
}
3739
}
3840

39-
def doPut[F[_]](key: String, value: V, ttl: Option[Duration])(implicit mode: Mode[F]): F[Any] = {
40-
mode.M.delay {
41-
val entry = Entry(value, ttl.map(toExpiryTime))
42-
underlying.put(key, entry)
43-
logCachePut(key, ttl)
41+
def doPut(key: String, value: V, ttl: Option[Duration]): F[Unit] =
42+
ttl.traverse(toExpiryTime).flatMap { expiry =>
43+
F.delay {
44+
val entry = Entry(value, expiry)
45+
underlying.put(key, entry)
46+
} *> logCachePut(key, ttl)
4447
}
45-
}
4648

47-
override def doRemove[F[_]](key: String)(implicit mode: Mode[F]): F[Any] =
48-
mode.M.delay(underlying.invalidate(key))
49+
override def doRemove(key: String): F[Unit] =
50+
F.delay(underlying.invalidate(key))
4951

50-
override def doRemoveAll[F[_]]()(implicit mode: Mode[F]): F[Any] =
51-
mode.M.delay(underlying.invalidateAll())
52+
override def doRemoveAll(): F[Unit] =
53+
F.delay(underlying.invalidateAll())
5254

53-
override def close[F[_]]()(implicit mode: Mode[F]): F[Any] = {
55+
override def close: F[Unit] = {
5456
// Nothing to do
55-
mode.M.pure(())
57+
F.unit
5658
}
5759

58-
private def toExpiryTime(ttl: Duration): Instant =
59-
Instant.now(clock).plus(ttl.toMillis, ChronoUnit.MILLIS)
60+
private def toExpiryTime(ttl: Duration): F[Instant] =
61+
clock.monotonic(TimeUnit.MILLISECONDS).map(Instant.ofEpochMilli(_).plusMillis(ttl.toMillis))
6062

6163
}
6264

6365
object CaffeineCache {
6466

6567
/**
66-
* Create a new Caffeine cache
68+
* Create a new Caffeine cache.
6769
*/
68-
def apply[V](implicit config: CacheConfig): CaffeineCache[V] =
69-
apply(Caffeine.newBuilder().build[String, Entry[V]]())
70+
def apply[F[_]: Sync: Clock, V](implicit config: CacheConfig): F[CaffeineCache[F, V]] =
71+
Sync[F].delay(Caffeine.newBuilder().build[String, Entry[V]]()).map(apply(_))
7072

7173
/**
7274
* Create a new cache utilizing the given underlying Caffeine cache.
7375
*
7476
* @param underlying a Caffeine cache
7577
*/
76-
def apply[V](underlying: CCache[String, Entry[V]])(implicit config: CacheConfig): CaffeineCache[V] =
78+
def apply[F[_]: Sync: Clock, V](
79+
underlying: CCache[String, Entry[V]]
80+
)(implicit config: CacheConfig): CaffeineCache[F, V] =
7781
new CaffeineCache(underlying)
7882

7983
}

0 commit comments

Comments
 (0)