(Split this out of #309 )
Problem
As detailed of #309 (there is a script showcasing this) we have an issue where even with pool_max_idle_time set we still have process leakage. I just found exactly why that is, in the docs of NimblePool.handle_ping/2 there is a disclaimer:
On lazy pools, if no worker is currently on the pool the callback will never be called. Therefore you can not rely on this callback to terminate empty lazy pools.
Finch is using lazy pools, starting a bunch of empty ones in the beginning (depending on pool count). If they never get a connection, they'll never be shut down.
The relevant code in NimblePool it's here:
defp do_check_idle_resources(resources, now_in_ms, state, new_resources, remaining_pings) do
case :queue.out(resources) do
{:empty, _} ->
{:ok, new_resources, state}
This isn't a problem when you know where your HTTP requests go. In my use case, I can't know where my HTTP requests will go in advance, there are infinite possibilities, and I need my default pool to be decently large as some of these will have a lot of traffic.
Solutions
I'm not sure about the best solution here, here are some rough ideas:
- We could ask/contribute to
nimble_pool an option to shut down empty pools, then the pools would shut down
- we could implement a strategy to shut down empty pools ourselves, feels a bit sad though (as NimblePool already has a
ping mechanic)
That said, with the current workings I'm also a bit worried about shutting down pools. If I'm not missing anything, let's say you configure a pool count of 15. You may shut down 14 pools via idle verification, one survives and now if a load spike comes that one pool has to deal with all the traffic as no new pools are started again. With a set count of pools in a... pool group (? not sure how to call it) it may be better to shut down all pools at once or none at all to avoid the scenario described.
That'd be different though if we used dynamic pool creation & closure - might open another issue about that though.
I'm happy to open an issue at NimblePool and suggest the change, but so far it seems very deliberate on their part. So, I'd first like to align on a way forward here.
As always, thank you a ton for providing us all with great and free open source software! 💚 🙏
(Split this out of #309 )
Problem
As detailed of #309 (there is a script showcasing this) we have an issue where even with
pool_max_idle_timeset we still have process leakage. I just found exactly why that is, in the docs ofNimblePool.handle_ping/2there is a disclaimer:Finch is using lazy pools, starting a bunch of empty ones in the beginning (depending on pool count). If they never get a connection, they'll never be shut down.
The relevant code in
NimblePoolit's here:This isn't a problem when you know where your HTTP requests go. In my use case, I can't know where my HTTP requests will go in advance, there are infinite possibilities, and I need my default pool to be decently large as some of these will have a lot of traffic.
Solutions
I'm not sure about the best solution here, here are some rough ideas:
nimble_poolan option to shut down empty pools, then the pools would shut downpingmechanic)That said, with the current workings I'm also a bit worried about shutting down pools. If I'm not missing anything, let's say you configure a pool count of 15. You may shut down 14 pools via idle verification, one survives and now if a load spike comes that one pool has to deal with all the traffic as no new pools are started again. With a set count of pools in a... pool group (? not sure how to call it) it may be better to shut down all pools at once or none at all to avoid the scenario described.
That'd be different though if we used dynamic pool creation & closure - might open another issue about that though.
I'm happy to open an issue at NimblePool and suggest the change, but so far it seems very deliberate on their part. So, I'd first like to align on a way forward here.
As always, thank you a ton for providing us all with great and free open source software! 💚 🙏