Permalink
Browse files

! cache: use util.Timestamp in ExpiringLruCache

The breaking changes here are:
  * ExpiringLruCache uses Durations instead of longs for timeouts
  * LruCache.apply doesn't allow Duration.Zero any more but instead Duration.Inf
    for not specifying any timeouts
  • Loading branch information...
jrudolph committed Sep 19, 2013
1 parent ab17f00 commit 23947205a5240567410052004a33044816750319
@@ -77,8 +77,8 @@ both featuring last-recently-used cache eviction semantics and both internally w
They difference between the two only consists of whether they support time-based entry expiration or not.
The easiest way to construct a cache instance is via the ``apply`` method of the ``LruCache`` object, which has the
-following signature and creates a new ``ExpiringLruCache`` or ``SimpleLruCache`` depending on whether a non-zero and
-finite ``timeToLive`` and/or ``timeToIdle`` is set or not:
+following signature and creates a new ``ExpiringLruCache`` or ``SimpleLruCache`` depending on whether
+``timeToLive`` and/or ``timeToIdle`` are finite (= expiring) or infinite:
.. includecode:: /../spray-caching/src/main/scala/spray/caching/LruCache.scala
:snippet: source-quote-LruCache-apply
@@ -97,7 +97,7 @@ ExpiringLruCache
This implementation has the same limited capacity behavior as the ``SimpleLruCache`` but in addition supports
time-to-live as well as time-to-idle expiration.
The former provides an upper limit to the time period an entry is allowed to remain in the cache while the latter
-limits the maximum time an entry is kept without having been accessed. If both values are non-zero the time-to-live
+limits the maximum time an entry is kept without having been accessed. If both values are finite the time-to-live
has to be strictly greater than the time-to-idle.
.. note:: Expired entries are only evicted upon next access (or by being thrown out by the capacity constraint), so
@@ -106,4 +106,4 @@ has to be strictly greater than the time-to-idle.
.. _Cache: https://github.com/spray/spray/blob/master/spray-caching/src/main/scala/spray/caching/Cache.scala
.. _SimpleLruCache and ExpiringLruCache: https://github.com/spray/spray/blob/master/spray-caching/src/main/scala/spray/caching/LruCache.scala
-.. _concurrentlinkedhashmap: http://code.google.com/p/concurrentlinkedhashmap/
+.. _concurrentlinkedhashmap: http://code.google.com/p/concurrentlinkedhashmap/
@@ -21,6 +21,7 @@ import scala.annotation.tailrec
import scala.concurrent.duration.Duration
import scala.concurrent.{ Promise, ExecutionContext, Future }
import scala.util.{ Failure, Success }
+import spray.util.Timestamp
object LruCache {
@@ -32,14 +33,19 @@ object LruCache {
*/
def apply[V](maxCapacity: Int = 500,
initialCapacity: Int = 16,
- timeToLive: Duration = Duration.Zero,
- timeToIdle: Duration = Duration.Zero): Cache[V] = {
+ timeToLive: Duration = Duration.Inf,
+ timeToIdle: Duration = Duration.Inf): Cache[V] = {
//#
- import Duration._
- def isNonZeroFinite(d: Duration) = d != Zero && d.isFinite
- def millis(d: Duration) = if (isNonZeroFinite(d)) d.toMillis else 0L
- if (isNonZeroFinite(timeToLive) || isNonZeroFinite(timeToIdle))
- new ExpiringLruCache[V](maxCapacity, initialCapacity, millis(timeToLive), millis(timeToIdle))
+ def check(dur: Duration, name: String) =
+ require(dur != Duration.Zero,
+ s"Behavior of LruCache.apply changed: Duration.Zero not allowed any more for $name parameter. To disable " +
+ "expiration use Duration.Inf instead of Duration.Zero")
+ // migration help
+ check(timeToLive, "timeToLive")
+ check(timeToIdle, "timeToIdle")
+
+ if (timeToLive.isFinite() || timeToIdle.isFinite())
+ new ExpiringLruCache[V](maxCapacity, initialCapacity, timeToLive, timeToIdle)
else
new SimpleLruCache[V](maxCapacity, initialCapacity)
}
@@ -100,14 +106,9 @@ final class SimpleLruCache[V](val maxCapacity: Int, val initialCapacity: Int) ex
* @param timeToIdle the time-to-idle in millis, zero for disabling tti-expiration
*/
final class ExpiringLruCache[V](maxCapacity: Long, initialCapacity: Int,
- timeToLive: Long, timeToIdle: Long) extends Cache[V] {
- require(timeToLive >= 0, "timeToLive must not be negative")
- require(timeToIdle >= 0, "timeToIdle must not be negative")
- require(timeToLive == 0 || timeToIdle == 0 || timeToLive > timeToIdle,
- "timeToLive must be greater than timeToIdle, if both are non-zero")
-
- private[this] val timeToLiveNanos = timeToLive * 1000000L
- private[this] val timeToIdleNanos = timeToIdle * 1000000L
+ timeToLive: Duration, timeToIdle: Duration) extends Cache[V] {
+ require(!timeToLive.isFinite || !timeToIdle.isFinite || timeToLive > timeToIdle,
+ s"timeToLive($timeToLive) must be greater than timeToIdle($timeToIdle)")
private[caching] val store = new ConcurrentLinkedHashMap.Builder[Any, Entry[V]]
.initialCapacity(initialCapacity)
@@ -167,20 +168,18 @@ final class ExpiringLruCache[V](maxCapacity: Long, initialCapacity: Int,
def size = store.size
- private def isAlive(entry: Entry[V]) = {
- val now = System.nanoTime()
- (timeToLiveNanos == 0 || (now - entry.created) < timeToLiveNanos) &&
- (timeToIdleNanos == 0 || (now - entry.lastAccessed) < timeToIdleNanos)
- }
+ private def isAlive(entry: Entry[V]) =
+ (entry.created + timeToLive).isFuture &&
+ (entry.lastAccessed + timeToIdle).isFuture
}
private[caching] class Entry[T](val promise: Promise[T]) {
- @volatile var created = System.nanoTime()
- @volatile var lastAccessed = System.nanoTime()
+ @volatile var created = Timestamp.now
+ @volatile var lastAccessed = Timestamp.now
def future = promise.future
def refresh(): Unit = {
// we dont care whether we overwrite a potentially newer value
- lastAccessed = System.nanoTime()
+ lastAccessed = Timestamp.now
}
override def toString = future.value match {
case Some(Success(value)) value.toString
@@ -131,7 +131,7 @@ class ExpiringLruCacheSpec extends Specification with NoTimeConversions {
step(system.shutdown())
def lruCache[T](maxCapacity: Int = 500, initialCapacity: Int = 16,
- timeToLive: Duration = Duration.Zero, timeToIdle: Duration = Duration.Zero) =
- new ExpiringLruCache[T](maxCapacity, initialCapacity, timeToLive.toMillis, timeToIdle.toMillis)
+ timeToLive: Duration = Duration.Inf, timeToIdle: Duration = Duration.Inf) =
+ new ExpiringLruCache[T](maxCapacity, initialCapacity, timeToLive, timeToIdle)
}
@@ -85,8 +85,8 @@ trait CachingDirectives {
}
}
- def routeCache(maxCapacity: Int = 500, initialCapacity: Int = 16, timeToLive: Duration = Duration.Zero,
- timeToIdle: Duration = Duration.Zero): Cache[RouteResponse] =
+ def routeCache(maxCapacity: Int = 500, initialCapacity: Int = 16, timeToLive: Duration = Duration.Inf,
+ timeToIdle: Duration = Duration.Inf): Cache[RouteResponse] =
LruCache(maxCapacity, initialCapacity, timeToLive, timeToIdle)
}

0 comments on commit 2394720

Please sign in to comment.