Skip to content

Commit 1b33e95

Browse files
committed
Port caffeine module
1 parent 908cca1 commit 1b33e95

File tree

2 files changed

+49
-37
lines changed

2 files changed

+49
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,87 @@
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
16+
import cats.Defer
1217

1318
/*
1419
* Thin wrapper around Caffeine.
1520
*
1621
* This cache implementation is synchronous.
1722
*/
18-
class CaffeineCache[V](val underlying: CCache[String, Entry[V]])(
23+
class CaffeineCache[F[_], V](val underlying: CCache[String, Entry[F, V]])(
1924
implicit val config: CacheConfig,
20-
clock: Clock = Clock.systemUTC()
21-
) extends AbstractCache[V] {
25+
clock: Clock[F],
26+
val F: Sync[F]
27+
) extends AbstractCache[F, V] {
28+
protected implicit val Defer: Defer[F] = F
2229

2330
override protected final val logger = Logger.getLogger(getClass.getName)
2431

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)
32+
def doGet(key: String): F[Option[V]] = {
33+
F.delay {
34+
Option(underlying.getIfPresent(key))
35+
}
36+
.flatMap(_.filterA(_.isExpired))
37+
.map(_.map(_.value))
38+
.flatTap { result =>
39+
F.delay {
40+
logCacheHitOrMiss(key, result)
41+
}
3342
}
34-
logCacheHitOrMiss(key, result)
35-
result
36-
}
3743
}
3844

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))
45+
def doPut(key: String, value: V, ttl: Option[Duration]): F[Any] = ttl.traverse(toExpiryTime).flatMap { expiry =>
46+
F.delay {
47+
val entry = Entry[F, V](value, expiry)
4248
underlying.put(key, entry)
4349
logCachePut(key, ttl)
4450
}
4551
}
4652

47-
override def doRemove[F[_]](key: String)(implicit mode: Mode[F]): F[Any] =
48-
mode.M.delay(underlying.invalidate(key))
53+
override def doRemove(key: String): F[Any] =
54+
F.delay(underlying.invalidate(key))
4955

50-
override def doRemoveAll[F[_]]()(implicit mode: Mode[F]): F[Any] =
51-
mode.M.delay(underlying.invalidateAll())
56+
override def doRemoveAll(): F[Any] =
57+
F.delay(underlying.invalidateAll())
5258

53-
override def close[F[_]]()(implicit mode: Mode[F]): F[Any] = {
59+
override def close: F[Any] = {
5460
// Nothing to do
55-
mode.M.pure(())
61+
F.pure(())
5662
}
5763

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

6167
}
6268

6369
object CaffeineCache {
6470

6571
/**
66-
* Create a new Caffeine cache
72+
* Create a new Caffeine cache.
6773
*/
68-
def apply[V](implicit config: CacheConfig): CaffeineCache[V] =
69-
apply(Caffeine.newBuilder().build[String, Entry[V]]())
74+
def apply[F[_]: Sync: Clock, V](implicit config: CacheConfig): F[CaffeineCache[F, V]] =
75+
Sync[F].delay(Caffeine.newBuilder().build[String, Entry[F, V]]()).map(apply(_))
7076

7177
/**
7278
* Create a new cache utilizing the given underlying Caffeine cache.
7379
*
7480
* @param underlying a Caffeine cache
7581
*/
76-
def apply[V](underlying: CCache[String, Entry[V]])(implicit config: CacheConfig): CaffeineCache[V] =
82+
def apply[F[_]: Sync: Clock, V](
83+
underlying: CCache[String, Entry[F, V]]
84+
)(implicit config: CacheConfig): CaffeineCache[F, V] =
7785
new CaffeineCache(underlying)
7886

7987
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package scalacache
22

3-
import java.time.{Clock, Instant}
3+
import java.time.{Instant}
4+
import cats.effect.Clock
5+
import java.util.concurrent.TimeUnit
6+
import cats.implicits._
7+
import language.higherKinds
8+
import cats.Functor
49

510
/**
611
* A cache entry with an optional expiry time
712
*/
8-
case class Entry[+A](value: A, expiresAt: Option[Instant]) {
13+
case class Entry[F[_], +A](value: A, expiresAt: Option[Instant]) {
914

1015
/**
1116
* Has the entry expired yet?
1217
*/
13-
def isExpired(implicit clock: Clock): Boolean =
14-
expiresAt.exists(_.isBefore(Instant.now(clock)))
15-
18+
def isExpired(implicit clock: Clock[F], functor: Functor[F]): F[Boolean] =
19+
clock.monotonic(TimeUnit.MILLISECONDS).map(Instant.ofEpochMilli(_)).map(now => expiresAt.exists(_.isBefore(now)))
1620
}

0 commit comments

Comments
 (0)