Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
162 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
src/main/scala/com/github/blemale/scaffeine/AsyncCache.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.github.blemale.scaffeine | ||
|
||
import java.util.concurrent.Executor | ||
|
||
import com.github.benmanes.caffeine.cache.{ AsyncCache => CaffeineAsyncCache } | ||
|
||
import scala.compat.java8.FunctionConverters._ | ||
import scala.compat.java8.FutureConverters._ | ||
import scala.concurrent.Future | ||
|
||
object AsyncCache { | ||
def apply[K, V](asyncCache: CaffeineAsyncCache[K, V]): AsyncCache[K, V] = | ||
new AsyncCache(asyncCache) | ||
} | ||
|
||
class AsyncCache[K, V](val underlying: CaffeineAsyncCache[K, V]) { | ||
|
||
/** | ||
* Returns the future associated with `key` in this cache, or `None` if there is no | ||
* cached future for `key`. | ||
* | ||
* @param key key whose associated value is to be returned | ||
* @return an option containing the current (existing or computed) future value to which the | ||
* specified key is mapped, or `None` if this map contains no mapping for the key | ||
*/ | ||
def getIfPresent(key: K): Option[Future[V]] = | ||
Option(underlying.getIfPresent(key)).map(_.toScala) | ||
|
||
/** | ||
* Returns the future associated with `key` in this cache, obtaining that value from | ||
* `mappingFunction` if necessary. This method provides a simple substitute for the | ||
* conventional "if cached, return; otherwise create, cache and return" pattern. | ||
* | ||
* @param key key with which the specified value is to be associated | ||
* @param mappingFunction the function to asynchronously compute a value | ||
* @return the current (existing or computed) future value associated with the specified key | ||
*/ | ||
def get(key: K, mappingFunction: K => V): Future[V] = | ||
underlying.get(key, asJavaFunction(mappingFunction)).toScala | ||
|
||
/** | ||
* Returns the future associated with `key` in this cache, obtaining that value from | ||
* `mappingFunction` if necessary. This method provides a simple substitute for the | ||
* conventional "if cached, return; otherwise create, cache and return" pattern. | ||
* | ||
* @param key key with which the specified value is to be associated | ||
* @param mappingFunction the function to asynchronously compute a value | ||
* @return the current (existing or computed) future value associated with the specified key | ||
* @throws java.lang.RuntimeException or Error if the mappingFunction does when constructing the future, | ||
* in which case the mapping is left unestablished | ||
*/ | ||
def getFuture(key: K, mappingFunction: K => Future[V]): Future[V] = | ||
underlying.get( | ||
key, | ||
asJavaBiFunction((k: K, _: Executor) => mappingFunction(k).toJava.toCompletableFuture) | ||
).toScala | ||
|
||
/** | ||
* Associates `value` with `key` in this cache. If the cache previously contained a | ||
* value associated with `key`, the old value is replaced by `value`. If the | ||
* asynchronous computation fails, the entry will be automatically removed. | ||
* | ||
* @param key key with which the specified value is to be associated | ||
* @param valueFuture value to be associated with the specified key | ||
*/ | ||
def put(key: K, valueFuture: Future[V]): Unit = | ||
underlying.put(key, valueFuture.toJava.toCompletableFuture) | ||
|
||
/** | ||
* Returns a view of the entries stored in this cache as a synchronous [[Cache]]. A | ||
* mapping is not present if the value is currently being loaded. Modifications made to the | ||
* synchronous cache directly affect the asynchronous cache. If a modification is made to a | ||
* mapping that is currently loading, the operation blocks until the computation completes. | ||
* | ||
* @return a thread-safe synchronous view of this cache | ||
*/ | ||
def synchronous(): Cache[K, V] = | ||
Cache(underlying.synchronous()) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
src/test/scala/com/github/blemale/scaffeine/AsyncCacheSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.github.blemale.scaffeine | ||
|
||
import org.scalatest.concurrent.ScalaFutures | ||
import org.scalatest.{ Matchers, OptionValues, WordSpec } | ||
|
||
import scala.concurrent.Future | ||
|
||
class AsyncCacheSpec | ||
extends WordSpec | ||
with Matchers | ||
with ScalaFutures | ||
with OptionValues { | ||
|
||
"AsyncCache" should { | ||
"get value if present" in { | ||
val cache = Scaffeine().buildAsync[String, String]() | ||
|
||
cache.put("foo", Future.successful("present")) | ||
val fooValue = cache.getIfPresent("foo") | ||
val barValue = cache.getIfPresent("bar") | ||
|
||
fooValue.value.futureValue should be("present") | ||
barValue should be(None) | ||
} | ||
|
||
"get or compute value" in { | ||
val cache = Scaffeine().buildAsync[String, String]() | ||
|
||
cache.put("foo", Future.successful("present")) | ||
val fooValue = cache.get("foo", k => "computed") | ||
val barValue = cache.get("bar", k => "computed") | ||
|
||
fooValue.futureValue should be("present") | ||
barValue.futureValue should be("computed") | ||
} | ||
|
||
"get or compute async value" in { | ||
val cache = Scaffeine().buildAsync[String, String]() | ||
|
||
cache.put("foo", Future.successful("present")) | ||
val fooValue = cache.getFuture("foo", k => Future.successful("computed")) | ||
val barValue = cache.getFuture("bar", k => Future.successful("computed")) | ||
|
||
fooValue.futureValue should be("present") | ||
barValue.futureValue should be("computed") | ||
} | ||
|
||
"put value" in { | ||
val cache = Scaffeine().buildAsync[String, String]() | ||
|
||
cache.put("foo", Future.successful("present")) | ||
val fooValue = cache.getIfPresent("foo") | ||
|
||
fooValue.value.futureValue should be("present") | ||
} | ||
|
||
"expose a synchronous view of itself" in { | ||
val cache = Scaffeine().buildAsync[String, String]() | ||
|
||
val synchronousCache = cache.synchronous() | ||
|
||
synchronousCache shouldBe a[Cache[_, _]] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters