Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions zio-cache/shared/src/main/scala/zio/cache/Cache.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package zio.cache

import zio.internal.MutableConcurrentQueue
import zio.{Exit, IO, Promise, UIO, URIO, ZIO}
import zio.{Exit, IO, Promise, Task, UIO, URIO, ZIO}

import java.time.{Duration, Instant}
import java.util.Map
Expand All @@ -25,7 +25,9 @@ import java.util.concurrent.atomic.{AtomicBoolean, LongAdder}
* the same key the lookup function will only be computed once and the result
* will be returned to all fibers.
*/
sealed abstract class Cache[-Key, +Error, +Value] {
sealed abstract class Cache[-Key, +Error, Value] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The loss of variance is a significant downside here. We should try to push this functionality into the Lookup function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rework it... Yeah, I encountered some problem with variance. Tried doing [V >: Value] but the underlying Hashmap/ConcurrentHashmap is invariant. As a compromise, I made Value invariant, hence the draft :)

Let me rework this. Thanks Adam.

@giordanoj giordanoj Jul 21, 2023

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamgfraser did you have any additional thoughts on how to push this into Lookup. I've been playing around with it a little without much luck. Tried adding a get function that accepts a custom/temporary lookup, but gets messy because you have to add that to the interface level as well


def add(key: Key, value: Value): Task[Boolean]

/**
* Returns statistics for this cache.
Expand Down Expand Up @@ -141,6 +143,12 @@ object Cache {

new Cache[Key, Error, Value] {

override def add(key: Key, value: Value): Task[Boolean] =
ZIO.effect {
val v = completeNowWith(key, Exit.Success(value))
map.putIfAbsent(key, v) eq null
}

override def cacheStats: UIO[CacheStats] =
ZIO.succeed(CacheStats(hits.longValue, misses.longValue, map.size))

Expand Down Expand Up @@ -251,20 +259,24 @@ object Cache {
override def size: UIO[Int] =
ZIO.succeed(map.size)

private def completeNowWith(key: Key, result: Exit[Error, Value]) = {
val now = Instant.now()

MapValue.Complete(
new MapKey(key),
result,
EntryStats(now),
now.plus(timeToLive(result))
)
}

private def lookupValueOf(key: Key, promise: Promise[Error, Value]): IO[Error, Value] =
lookup(key)
.provide(environment)
.run
.flatMap { lookupResult =>
val now = Instant.now()
val completedResult = MapValue.Complete(
new MapKey(key),
lookupResult,
EntryStats(now),
now.plus(timeToLive(lookupResult))
)

map.put(key, completedResult)
map.put(key, completeNowWith(key, lookupResult))

promise.done(lookupResult) *>
ZIO.done(lookupResult)
}
Expand Down
13 changes: 13 additions & 0 deletions zio-cache/shared/src/test/scala/zio/cache/CacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@ object CacheSpec extends DefaultRunnableSpec {
}
}
),
suite("`add` method")(
testM("") {
checkM(Gen.anyInt) { salt =>
val indices = 1 to 10
for {
cache <- Cache.make(capacity = 10, Duration.Infinity, Lookup(hash(salt)))
addResults <- ZIO.foreach(indices)(n => cache.add(n, n))
getResults <- ZIO.foreach(indices)(cache.get)
} yield assertTrue(addResults.toSet == Set(true)) &&
assertTrue(getResults.toList == indices.toList)
}
}
),
testM("size") {
checkM(Gen.anyInt) { salt =>
for {
Expand Down