Skip to content
This repository has been archived by the owner on Apr 24, 2024. It is now read-only.

Commit

Permalink
! cache: use util.Timestamp in ExpiringLruCache
Browse files Browse the repository at this point in the history
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 25, 2013
1 parent ab17f00 commit 2394720
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 31 deletions.
8 changes: 4 additions & 4 deletions docs/documentation/spray-caching/index.rst
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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/
45 changes: 22 additions & 23 deletions spray-caching/src/main/scala/spray/caching/LruCache.scala
Expand Up @@ -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 {

Expand All @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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)

}
Expand Up @@ -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)
}

Expand Down

0 comments on commit 2394720

Please sign in to comment.