/
AbstractCache.scala
174 lines (145 loc) · 4.83 KB
/
AbstractCache.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package scalacache
import scala.concurrent.duration.Duration
import scala.language.higherKinds
/**
* An abstract implementation of [[CacheAlg]] that takes care of
* some things that are common across all concrete implementations.
*
* If you are writing a cache implementation, you probably want to
* extend this trait rather than extending [[CacheAlg]] directly.
*
* @tparam V The value of types stored in the cache.
*/
trait AbstractCache[V] extends Cache[V] with LoggingSupport {
// GET
protected def doGet[F[_]](key: String)(implicit mode: Mode[F]): F[Option[V]]
private def checkFlagsAndGet[F[_]](key: String)(implicit mode: Mode[F], flags: Flags): F[Option[V]] = {
if (flags.readsEnabled) {
doGet(key)
} else {
if (logger.isDebugEnabled) {
logger.debug(s"Skipping cache GET because cache reads are disabled. Key: $key")
}
mode.M.pure(None)
}
}
final override def get[F[_]](keyParts: Any*)(implicit mode: Mode[F], flags: Flags): F[Option[V]] = {
val key = toKey(keyParts: _*)
checkFlagsAndGet(key)
}
// PUT
protected def doPut[F[_]](key: String, value: V, ttl: Option[Duration])(implicit mode: Mode[F]): F[Any]
private def checkFlagsAndPut[F[_]](key: String, value: V, ttl: Option[Duration])(
implicit mode: Mode[F],
flags: Flags
): F[Any] = {
if (flags.writesEnabled) {
doPut(key, value, ttl)
} else {
if (logger.isDebugEnabled) {
logger.debug(s"Skipping cache PUT because cache writes are disabled. Key: $key")
}
mode.M.pure(())
}
}
final override def put[F[_]](
keyParts: Any*
)(value: V, ttl: Option[Duration])(implicit mode: Mode[F], flags: Flags): F[Any] = {
val key = toKey(keyParts: _*)
val finiteTtl = ttl.filter(_.isFinite) // discard Duration.Inf, Duration.Undefined
checkFlagsAndPut(key, value, finiteTtl)
}
// REMOVE
protected def doRemove[F[_]](key: String)(implicit mode: Mode[F]): F[Any]
final override def remove[F[_]](keyParts: Any*)(implicit mode: Mode[F]): F[Any] =
doRemove(toKey(keyParts: _*))
// REMOVE ALL
protected def doRemoveAll[F[_]]()(implicit mode: Mode[F]): F[Any]
final override def removeAll[F[_]]()(implicit mode: Mode[F]): F[Any] =
doRemoveAll()
// CACHING
final override def caching[F[_]](
keyParts: Any*
)(ttl: Option[Duration] = None)(f: => V)(implicit mode: Mode[F], flags: Flags): F[V] = {
val key = toKey(keyParts: _*)
_caching(key, ttl, f)
}
override def cachingF[F[_]](
keyParts: Any*
)(ttl: Option[Duration] = None)(f: => F[V])(implicit mode: Mode[F], flags: Flags): F[V] = {
val key = toKey(keyParts: _*)
_cachingF(key, ttl, f)
}
// MEMOIZE
override def cachingForMemoize[F[_]](
baseKey: String
)(ttl: Option[Duration] = None)(f: => V)(implicit mode: Mode[F], flags: Flags): F[V] = {
val key = config.cacheKeyBuilder.stringToCacheKey(baseKey)
_caching(key, ttl, f)
}
override def cachingForMemoizeF[F[_]](
baseKey: String
)(ttl: Option[Duration])(f: => F[V])(implicit mode: Mode[F], flags: Flags): F[V] = {
val key = config.cacheKeyBuilder.stringToCacheKey(baseKey)
_cachingF(key, ttl, f)
}
private def _caching[F[_]](key: String, ttl: Option[Duration], f: => V)(
implicit mode: Mode[F],
flags: Flags
): F[V] = {
import mode._
M.flatMap {
M.handleNonFatal(checkFlagsAndGet(key)) { e =>
if (logger.isWarnEnabled) {
logger.warn(s"Failed to read from cache. Key = $key", e)
}
None
}
} {
case Some(valueFromCache) =>
M.pure(valueFromCache)
case None =>
val calculatedValue = f
M.map {
M.handleNonFatal {
checkFlagsAndPut(key, calculatedValue, ttl)
} { e =>
if (logger.isWarnEnabled) {
logger.warn(s"Failed to write to cache. Key = $key", e)
}
}
}(_ => calculatedValue)
}
}
private def _cachingF[F[_]](key: String, ttl: Option[Duration], f: => F[V])(
implicit mode: Mode[F],
flags: Flags
): F[V] = {
import mode._
M.flatMap {
M.handleNonFatal(checkFlagsAndGet(key)) { e =>
if (logger.isWarnEnabled) {
logger.warn(s"Failed to read from cache. Key = $key", e)
}
None
}
} {
case Some(valueFromCache) =>
M.pure(valueFromCache)
case None =>
M.flatMap(f) { calculatedValue =>
M.map {
M.handleNonFatal {
checkFlagsAndPut(key, calculatedValue, ttl)
} { e =>
if (logger.isWarnEnabled) {
logger.warn(s"Failed to write to cache. Key = $key", e)
}
}
}(_ => calculatedValue)
}
}
}
private def toKey(keyParts: Any*): String =
config.cacheKeyBuilder.toCacheKey(keyParts)
}