Skip to content

Commit

Permalink
Added method to cancel timed cache invalidation
Browse files Browse the repository at this point in the history
  • Loading branch information
mjftw committed Jun 28, 2021
1 parent 96ac206 commit 22946a7
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/main/scala/org/github/mjftw/scache/Cache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ trait Cache[F[_], K, V] {
def put(key: K, value: V): F[Unit]
def putWithExpiry(key: K, value: V, expireAfter: FiniteDuration): F[Unit]
def get(key: K): F[Option[V]]
def cancelExpiry(key: K): F[Boolean]
}

object Cache {
Expand Down Expand Up @@ -38,6 +39,24 @@ object Cache {
/** Retrieve a value from the cache */
def get(key: K): F[Option[V]] = ref.get.map(_.get(key).map { case (value, _) => value })

/** Prevent a cache entry at a given key from expiring.
* Returns true if the key was found in the cache, false otherwise.
*/
def cancelExpiry(key: K): F[Boolean] =
for {
oldCache <- ref.getAndUpdate(cache =>
cache.get(key) match {
case Some((value, _)) =>
cache + (key -> Tuple2(value, Fiber(Sync[F].unit, Sync[F].unit)))
case None => cache
}
)
wasFound <- oldCache.get(key) match {
case Some((_, oldFiber)) => oldFiber.cancel.as(true)
case None => Sync[F].unit.as(false)
}
} yield wasFound

/** Put replace the value and fiber at a key and cancel the old fiber */
private def putValueAndFiber(key: K, value: V, fiber: CacheFiber[F]) =
for {
Expand Down
41 changes: 41 additions & 0 deletions src/test/scala/org/github/mjftw/scache/CacheSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,45 @@ class CacheSpec extends AnyFlatSpec with Matchers {
result.value should be(Some(Success((Some(1), None))))
}

"cancelExpiry" should "remove the invalidation timer on cache entries" in {
val result = (for {
cache <- Cache.make[IO, String, Int]
_ <- cache.putWithExpiry("foo", 1, 1.second)
_ <- IO.sleep(500.millis)
_ <- cache.cancelExpiry("foo")
_ <- IO.sleep(1.second)
x <- cache.get("foo")
} yield x).unsafeToFuture

ctx.tick(2.second)

result.value should be(Some(Success(Some(1))))
}

it should "return true if the key was not found in the cache" in {
val result = (for {
cache <- Cache.make[IO, String, Int]
_ <- cache.putWithExpiry("foo", 1, 1.second)
_ <- IO.sleep(500.millis)
x <- cache.cancelExpiry("foo")
} yield x).unsafeToFuture

ctx.tick(2.second)

result.value should be(Some(Success(true)))
}

it should "return false if the key was not found in the cache" in {
val result = (for {
cache <- Cache.make[IO, String, Int]
_ <- cache.putWithExpiry("bar", 1, 1.second)
_ <- IO.sleep(500.millis)
x <- cache.cancelExpiry("foo")
} yield x).unsafeToFuture

ctx.tick(2.second)

result.value should be(Some(Success(false)))
}

}

0 comments on commit 22946a7

Please sign in to comment.