Skip to content

Commit

Permalink
Merge pull request #299 from sasa1977/support-manual-pool-termination
Browse files Browse the repository at this point in the history
Support manual pool termination
  • Loading branch information
sneako authored Feb 17, 2025
2 parents 93dd445 + f89d93c commit d7a2354
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
32 changes: 32 additions & 0 deletions lib/finch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -656,4 +656,36 @@ defmodule Finch do
{:error, :not_found}
end
end

@doc """
Stops the pool of processes associated with the given scheme, host, port (aka SHP).
This function can be invoked to manually stop the pool to the given SHP when you know it's not
going to be used anymore.
Note that this function is not safe with respect to concurrent requests. Invoking it while
another request to the same SHP is taking place might result in the failure of that request. It
is the responsibility of the client to ensure that no request to the same SHP is taking place
while this function is being invoked.
"""
@spec stop_pool(name(), url :: String.t() | scheme_host_port()) :: :ok | {:error, :not_found}
def stop_pool(finch_name, url) when is_binary(url) do
{s, h, p, _, _} = Request.parse_url(url)
stop_pool(finch_name, {s, h, p})
end

def stop_pool(finch_name, shp) when is_tuple(shp) do
case PoolManager.all_pool_instances(finch_name, shp) do
[] ->
{:error, :not_found}

children ->
Enum.each(
children,
fn {pid, _module} ->
DynamicSupervisor.terminate_child(pool_supervisor_name(finch_name), pid)
end
)
end
end
end
4 changes: 3 additions & 1 deletion lib/finch/pool_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ defmodule Finch.PoolManager do
end

def lookup_pool(registry, key) do
case Registry.lookup(registry, key) do
case all_pool_instances(registry, key) do
[] ->
:none

Expand All @@ -60,6 +60,8 @@ defmodule Finch.PoolManager do
end
end

def all_pool_instances(registry, key), do: Registry.lookup(registry, key)

def start_pools(registry_name, shp) do
{:ok, config} = Registry.meta(registry_name, :config)
GenServer.call(config.manager_name, {:start_pools, shp})
Expand Down
58 changes: 58 additions & 0 deletions test/finch_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,64 @@ defmodule FinchTest do
end
end

describe "get_pool_status/2" do
test "fails if the pool doesn't exist", %{finch_name: finch_name} do
start_supervised!({Finch, name: finch_name})
assert Finch.stop_pool(finch_name, "http://unknown.url/") == {:error, :not_found}
end

test "succeeds with a string url", %{bypass: bypass, finch_name: finch_name} do
start_supervised!(
{Finch, name: finch_name, pools: %{default: [start_pool_metrics?: true, count: 2]}}
)

Bypass.expect_once(bypass, "GET", "/", fn conn -> Plug.Conn.send_resp(conn, 200, "OK") end)

url = endpoint(bypass)
{:ok, %{status: 200}} = Finch.build(:get, url) |> Finch.request(finch_name)

assert Finch.stop_pool(finch_name, url) == :ok

assert pool_stopped?(finch_name, url)
end

test "succeeds with an shp tuple", %{bypass: bypass, finch_name: finch_name} do
start_supervised!(
{Finch, name: finch_name, pools: %{default: [start_pool_metrics?: true, count: 2]}}
)

Bypass.expect_once(bypass, "GET", "/", fn conn -> Plug.Conn.send_resp(conn, 200, "OK") end)

url = endpoint(bypass)
{:ok, %{status: 200}} = Finch.build(:get, url) |> Finch.request(finch_name)

{s, h, p, _, _} = Finch.Request.parse_url(url)

assert Finch.stop_pool(finch_name, {s, h, p}) == :ok
assert pool_stopped?(finch_name, {s, h, p})
end

defp pool_stopped?(finch_name, url) do
# Need to use this pattern because the pools may linger on for a short while in the registry.
eventually(
fn -> Finch.get_pool_status(finch_name, url) == {:error, :not_found} end,
100,
50
)
end

defp eventually(fun, _backoff, 0), do: fun.()

defp eventually(fun, backoff, retries) do
if fun.() do
true
else
Process.sleep(backoff)
eventually(fun, backoff, retries - 1)
end
end
end

defp get_pools(name, shp) do
Registry.lookup(name, shp)
end
Expand Down

0 comments on commit d7a2354

Please sign in to comment.