|
1 | 1 | package scalacache.caffeine
|
2 | 2 |
|
3 | 3 | import java.time.temporal.ChronoUnit
|
4 |
| -import java.time.{Clock, Instant} |
| 4 | +import java.time.{Instant} |
5 | 5 |
|
6 | 6 | import com.github.benmanes.caffeine.cache.{Caffeine, Cache => CCache}
|
7 |
| - |
| 7 | +import cats.effect.Clock |
8 | 8 | import scalacache.logging.Logger
|
9 |
| -import scalacache.{AbstractCache, CacheConfig, Entry, Mode} |
| 9 | +import scalacache.{AbstractCache, CacheConfig, Entry} |
10 | 10 | import scala.concurrent.duration.Duration
|
11 | 11 | 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 |
12 | 17 |
|
13 | 18 | /*
|
14 | 19 | * Thin wrapper around Caffeine.
|
15 | 20 | *
|
16 | 21 | * This cache implementation is synchronous.
|
17 | 22 | */
|
18 |
| -class CaffeineCache[V](val underlying: CCache[String, Entry[V]])( |
| 23 | +class CaffeineCache[F[_], V](val underlying: CCache[String, Entry[F, V]])( |
19 | 24 | 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 |
22 | 29 |
|
23 | 30 | override protected final val logger = Logger.getLogger(getClass.getName)
|
24 | 31 |
|
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 | + } |
33 | 42 | }
|
34 |
| - logCacheHitOrMiss(key, result) |
35 |
| - result |
36 |
| - } |
37 | 43 | }
|
38 | 44 |
|
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) |
42 | 48 | underlying.put(key, entry)
|
43 | 49 | logCachePut(key, ttl)
|
44 | 50 | }
|
45 | 51 | }
|
46 | 52 |
|
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)) |
49 | 55 |
|
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()) |
52 | 58 |
|
53 |
| - override def close[F[_]]()(implicit mode: Mode[F]): F[Any] = { |
| 59 | + override def close: F[Any] = { |
54 | 60 | // Nothing to do
|
55 |
| - mode.M.pure(()) |
| 61 | + F.pure(()) |
56 | 62 | }
|
57 | 63 |
|
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)) |
60 | 66 |
|
61 | 67 | }
|
62 | 68 |
|
63 | 69 | object CaffeineCache {
|
64 | 70 |
|
65 | 71 | /**
|
66 |
| - * Create a new Caffeine cache |
| 72 | + * Create a new Caffeine cache. |
67 | 73 | */
|
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(_)) |
70 | 76 |
|
71 | 77 | /**
|
72 | 78 | * Create a new cache utilizing the given underlying Caffeine cache.
|
73 | 79 | *
|
74 | 80 | * @param underlying a Caffeine cache
|
75 | 81 | */
|
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] = |
77 | 85 | new CaffeineCache(underlying)
|
78 | 86 |
|
79 | 87 | }
|
0 commit comments