diff --git a/src/main/scala/com/evolutiongaming/scache/ExpiringCache.scala b/src/main/scala/com/evolutiongaming/scache/ExpiringCache.scala index a974a63..158e08b 100644 --- a/src/main/scala/com/evolutiongaming/scache/ExpiringCache.scala +++ b/src/main/scala/com/evolutiongaming/scache/ExpiringCache.scala @@ -4,9 +4,8 @@ import cats.effect.{Clock, Ref, Resource, Temporal} import cats.effect.syntax.all.* import cats.kernel.CommutativeMonoid import cats.syntax.all.* -import cats.{Applicative, MonadThrow, Monoid, Parallel} +import cats.{Applicative, Monad, MonadThrow, Monoid} import com.evolutiongaming.catshelper.ClockHelper.* -import com.evolutiongaming.catshelper.ParallelHelper.* import com.evolutiongaming.catshelper.Schedule import scala.concurrent.duration.* @@ -15,7 +14,7 @@ object ExpiringCache { type Timestamp = Long - private[scache] def of[F[_]: Parallel, K, V]( + private[scache] def of[F[_], K, V]( config: Config[F, K, V] )(implicit G: Temporal[F]): Resource[F, Cache[F, K, V]] = { @@ -76,7 +75,7 @@ object ExpiringCache { entries .sortBy(_.timestamp) .take(maxSize / 10) - .parFoldMap1 { elem => remove(elem.key) } + .foldMapM { elem => remove(elem.key) } } } @@ -88,7 +87,7 @@ object ExpiringCache { for { entryRefs <- ref.get - result <- entryRefs.parFoldMap1 { case (key, entryRef) => removeExpired(key, entryRef) } + result <- entryRefs.foldMapM { case (key, entryRef) => removeExpired(key, entryRef) } _ <- config .maxSize .foldMapM { maxSize => notExceedMaxSize(maxSize) } @@ -103,7 +102,7 @@ object ExpiringCache { ref .get .flatMap { entryRefs => - entryRefs.parFoldMap1 { case (key, entryRef) => + entryRefs.foldMapM { case (key, entryRef) => entryRef .get .flatMap { value => @@ -349,4 +348,18 @@ object ExpiringCache { expireAfterWrite: Option[FiniteDuration] = None, maxSize: Option[Int] = None, refresh: Option[Refresh[K, F[Option[V]]]] = None) + + + private implicit class MapOps[K, V](val self: Map[K, V]) extends AnyVal { + def foldMapM[F[_]: Monad, A: Monoid](f: (K, V) => F[A]): F[A] = { + self.foldLeft(Monoid[A].empty.pure[F]) { case (a, (k, v)) => + for { + a <- a + b <- f(k, v) + } yield { + a.combine(b) + } + } + } + } } \ No newline at end of file diff --git a/src/test/scala/com/evolutiongaming/scache/ExpiringCacheSpec.scala b/src/test/scala/com/evolutiongaming/scache/ExpiringCacheSpec.scala index 618665f..422ee23 100644 --- a/src/test/scala/com/evolutiongaming/scache/ExpiringCacheSpec.scala +++ b/src/test/scala/com/evolutiongaming/scache/ExpiringCacheSpec.scala @@ -1,6 +1,5 @@ package com.evolutiongaming.scache -import cats.Parallel import cats.effect.* import cats.syntax.all.* import com.evolutiongaming.scache.IOSuite.* @@ -44,7 +43,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { `refresh removes entry`[IO].run() } - private def expireRecords[F[_] : Async : Parallel] = { + private def expireRecords[F[_] : Async] = { ExpiringCache.of[F, Int, Int](ExpiringCache.Config(expireAfterRead = 100.millis)).use { cache => for { @@ -61,7 +60,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } } - private def `expire created entries`[F[_] : Async : Parallel] = { + private def `expire created entries`[F[_] : Async] = { val config = ExpiringCache.Config[F, Int, Int]( expireAfterRead = 1.minute, expireAfterWrite = 150.millis.some) @@ -79,7 +78,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } } - private def notExpireUsedRecords[F[_] : Async : Parallel] = { + private def notExpireUsedRecords[F[_] : Async] = { ExpiringCache.of[F, Int, Int](ExpiringCache.Config(50.millis)).use { cache => val touch = for { _ <- Temporal[F].sleep(10.millis) @@ -105,7 +104,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } - private def notExceedMaxSize[F[_] : Async : Parallel] = { + private def notExceedMaxSize[F[_] : Async] = { val config = ExpiringCache.Config[F, Int, Int]( expireAfterRead = 100.millis, expireAfterWrite = 100.millis.some, @@ -123,7 +122,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } } - private def refreshPeriodically[F[_] : Async : Parallel] = { + private def refreshPeriodically[F[_] : Async] = { val refresh = ExpiringCache.Refresh[Int](100.millis) { _.some.pure[F] } val config = ExpiringCache.Config( expireAfterRead = 1.minute, @@ -153,7 +152,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } } - private def refreshDoesNotTouch[F[_] : Async : Parallel] = { + private def refreshDoesNotTouch[F[_] : Async] = { val refresh = ExpiringCache.Refresh[Int](100.millis) { _.some.pure[F] } val config = ExpiringCache.Config( @@ -189,7 +188,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } } - private def refreshFails[F[_] : Async : Parallel] = { + private def refreshFails[F[_] : Async] = { def valueOf(ref: Ref[F, Int]) = { (_: Int) => { @@ -237,7 +236,7 @@ class ExpiringCacheSpec extends AsyncFunSuite with Matchers { } yield result } - def `refresh removes entry`[F[_] : Async : Parallel] = { + def `refresh removes entry`[F[_] : Async] = { val refresh = ExpiringCache.Refresh[Int](100.millis) { _ => none[Int].pure[F] } val config = ExpiringCache.Config(