diff --git a/core/src/main/scala/org/typelevel/keypool/KeyPool.scala b/core/src/main/scala/org/typelevel/keypool/KeyPool.scala index 6d450681..7159aae9 100644 --- a/core/src/main/scala/org/typelevel/keypool/KeyPool.scala +++ b/core/src/main/scala/org/typelevel/keypool/KeyPool.scala @@ -295,11 +295,19 @@ object KeyPool { } } + def createWithPermit: Resource[F, B] = + kp.kpMaxTotalSem.tryPermit.flatMap { + case true => + kp.kpRes(k) + case false => + ApplicativeThrow[Resource[F, *]].raiseError( + new NoSuchElementException("Pool is maxed out on idle connections for other keys")) + } + for { - _ <- kp.kpMaxTotalSem.permit optR <- Resource.eval(kp.kpVar.modify(go)) releasedState <- Resource.eval(Ref[F].of[Reusable](kp.kpDefaultReuseState)) - resource <- Resource.make(optR.fold(kp.kpRes(k).allocated)(r => Applicative[F].pure(r))) { + resource <- Resource.make(optR.fold(createWithPermit.allocated)(r => Applicative[F].pure(r))) { resource => for { reusable <- releasedState.get diff --git a/core/src/test/scala/org/typelevel/keypool/KeyPoolSpec.scala b/core/src/test/scala/org/typelevel/keypool/KeyPoolSpec.scala index 8a4903b0..b33f823d 100644 --- a/core/src/test/scala/org/typelevel/keypool/KeyPoolSpec.scala +++ b/core/src/test/scala/org/typelevel/keypool/KeyPoolSpec.scala @@ -176,7 +176,29 @@ class KeyPoolSpec extends CatsEffectSuite { } } + test("Do not count idle resources toward maxTotal".only) { + final case class Stats(active: Int, highWater: Int) { + def inc = + copy(active = active + 1, highWater = math.max(active + 1, highWater)) + def dec = + copy(active = active - 1) + } + IO.ref(Stats(0, 0)).flatMap(active => + KeyPool.Builder((i: Int) => + Resource.make(active.update(_.inc))(i => active.update(_.dec))) + .withMaxTotal(5) + .withMaxIdle(5) + .build + .use(keypool => + keypool.take(0).replicateA(5).use_ *> + keypool.take(1).replicateA(5).use_ + ) + .flatMap(_ => active.get.map(_.highWater)) + .flatTap(IO.println) + ) + .map(i => assert(i <= 5, s"Active connections maxed out at $i")) + } + private def nothing(ref: Ref[IO, Int]): IO[Unit] = ref.get.void - }