From 59f2ef6f3ca513c30f46da10721000a2ac485646 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Wed, 15 Apr 2026 14:36:30 +0200 Subject: [PATCH 1/2] Add :pool_tag option to support Finch tagged pools --- lib/req.ex | 4 ++++ lib/req/finch.ex | 8 ++++++-- lib/req/steps.ex | 5 +++++ test/req/finch_test.exs | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/req.ex b/lib/req.ex index fd9938de..14fc6cab 100644 --- a/lib/req.ex +++ b/lib/req.ex @@ -471,6 +471,10 @@ defmodule Req do * `:unix_socket` - if set, connect through the given UNIX domain socket. + * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. + Defaults to `:default`. This allows routing requests to different pools for the same + host. See `Finch.Pool.new/2` for more information on configuring tagged pools. + * `:pool_max_idle_time` - the maximum number of milliseconds that a pool can be idle before being terminated, used only by HTTP1 pools. Defaults to `:infinity`. diff --git a/lib/req/finch.ex b/lib/req/finch.ex index 8ebf0b8f..b44a72f3 100644 --- a/lib/req/finch.ex +++ b/lib/req/finch.ex @@ -76,9 +76,13 @@ defmodule Req.Finch do {:stream, enumerable} end + build_opts = + request.options + |> Map.take([:unix_socket, :pool_tag]) + |> Enum.to_list() + finch_request = - Finch.build(request.method, request.url, request_headers, body) - |> Map.replace!(:unix_socket, request.options[:unix_socket]) + Finch.build(request.method, request.url, request_headers, body, build_opts) |> add_private_options(request.options[:finch_private]) finch_options = diff --git a/lib/req/steps.ex b/lib/req/steps.ex index b7c2e017..1d61ccbb 100644 --- a/lib/req/steps.ex +++ b/lib/req/steps.ex @@ -62,6 +62,7 @@ defmodule Req.Steps do :receive_timeout, :pool_timeout, :unix_socket, + :pool_tag, :pool_max_idle_time, # TODO: Remove on Req 1.0 @@ -852,6 +853,10 @@ defmodule Req.Steps do * `:unix_socket` - if set, connect through the given UNIX domain socket. + * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. + Defaults to `:default`. This allows routing requests to different pools for the same + host. See `Finch.Pool.new/2` for more information on configuring tagged pools. + * `:pool_max_idle_time` - the maximum number of milliseconds that a pool can be idle before being terminated, used only by HTTP1 pools. Default to `:infinity`. diff --git a/test/req/finch_test.exs b/test/req/finch_test.exs index cc042b9a..65de4577 100644 --- a/test/req/finch_test.exs +++ b/test/req/finch_test.exs @@ -650,5 +650,41 @@ defmodule Req.FinchTest do conn_opts: [transport_opts: [timeout: 0, inet6: true, cacerts: []]] ] end + + test ":pool_tag is forwarded to Finch request" do + %{url: url} = + start_http_server(fn conn -> + Plug.Conn.send_resp(conn, 200, "ok") + end) + + pid = self() + + fun = fn req, finch_request, finch_name, finch_opts -> + send(pid, {:pool_tag, finch_request.pool_tag}) + {:ok, resp} = Finch.request(finch_request, finch_name, finch_opts) + {req, Req.Response.new(status: resp.status, headers: resp.headers, body: resp.body)} + end + + assert Req.get!(url, finch_request: fun, pool_tag: :bulk).body == "ok" + assert_received {:pool_tag, :bulk} + end + + test ":pool_tag defaults to :default" do + %{url: url} = + start_http_server(fn conn -> + Plug.Conn.send_resp(conn, 200, "ok") + end) + + pid = self() + + fun = fn req, finch_request, finch_name, finch_opts -> + send(pid, {:pool_tag, finch_request.pool_tag}) + {:ok, resp} = Finch.request(finch_request, finch_name, finch_opts) + {req, Req.Response.new(status: resp.status, headers: resp.headers, body: resp.body)} + end + + assert Req.get!(url, finch_request: fun).body == "ok" + assert_received {:pool_tag, :default} + end end end From 86744c763baf152d2ac1ff69ff0ff9910c168693 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Fri, 15 May 2026 23:59:49 +0200 Subject: [PATCH 2/2] Refactor :finch option to accept {name, opts} tuple for pool_tag/unix_socket Align with the new Finch API where pool_tag and unix_socket are build options passed to Finch.build/5. Instead of exposing :pool_tag as a top-level Req option, nest it under the :finch tuple: Req.get!(url, finch: {MyFinch, pool_tag: :bulk}) Req.get!(url, finch: {MyFinch, unix_socket: "/tmp/sock"}) --- lib/req.ex | 14 ++++++++++---- lib/req/finch.ex | 32 +++++++++++++++++++++++++------- lib/req/steps.ex | 15 ++++++++++----- test/req/finch_test.exs | 2 +- 4 files changed, 46 insertions(+), 17 deletions(-) diff --git a/lib/req.ex b/lib/req.ex index 14fc6cab..b174c478 100644 --- a/lib/req.ex +++ b/lib/req.ex @@ -441,6 +441,16 @@ defmodule Req do Finch options ([`run_finch`](`Req.Steps.run_finch/1`) step), see `Finch.start_link/1` for options: * `:finch` - the Finch pool to use. Defaults to pool automatically started by `Req`. + Can be either a pool name (atom) or a `{name, opts}` tuple where `opts` can include: + + * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. + Defaults to `:default`. This allows routing requests to different pools for the same + host. See `Finch.Pool.new/2` for more information on configuring tagged pools. + + Examples: + + Req.get!("https://api.example.com/data", finch: MyFinch) + Req.get!("https://api.example.com/data", finch: {MyFinch, pool_tag: :bulk}) * `:connect_options` - dynamically starts (or re-uses already started) Finch pool with the given connection options (see `Mint.HTTP.connect/4` for options): @@ -471,10 +481,6 @@ defmodule Req do * `:unix_socket` - if set, connect through the given UNIX domain socket. - * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. - Defaults to `:default`. This allows routing requests to different pools for the same - host. See `Finch.Pool.new/2` for more information on configuring tagged pools. - * `:pool_max_idle_time` - the maximum number of milliseconds that a pool can be idle before being terminated, used only by HTTP1 pools. Defaults to `:infinity`. diff --git a/lib/req/finch.ex b/lib/req/finch.ex index b44a72f3..0e67d593 100644 --- a/lib/req/finch.ex +++ b/lib/req/finch.ex @@ -76,10 +76,7 @@ defmodule Req.Finch do {:stream, enumerable} end - build_opts = - request.options - |> Map.take([:unix_socket, :pool_tag]) - |> Enum.to_list() + build_opts = finch_build_opts(request) finch_request = Finch.build(request.method, request.url, request_headers, body, build_opts) @@ -415,16 +412,37 @@ defmodule Req.Finch do end end + defp finch_build_opts(request) do + pool_tag_opts = + case request.options[:finch] do + {_name, opts} when is_list(opts) -> + Keyword.take(opts, [:pool_tag]) + + _ -> + [] + end + + unix_socket_opts = + request.options + |> Map.take([:unix_socket]) + |> Enum.to_list() + + pool_tag_opts ++ unix_socket_opts + end + defp finch_name(request) do custom_options? = Map.has_key?(request.options, :connect_options) or Map.has_key?(request.options, :inet6) cond do - name = request.options[:finch] -> + finch_opt = request.options[:finch] -> if Map.has_key?(request.options, :connect_options) do raise ArgumentError, "cannot set both :finch and :connect_options" - else - name + end + + case finch_opt do + {name, _opts} -> name + name -> name end custom_options? -> diff --git a/lib/req/steps.ex b/lib/req/steps.ex index 1d61ccbb..dacd9b49 100644 --- a/lib/req/steps.ex +++ b/lib/req/steps.ex @@ -62,7 +62,6 @@ defmodule Req.Steps do :receive_timeout, :pool_timeout, :unix_socket, - :pool_tag, :pool_max_idle_time, # TODO: Remove on Req 1.0 @@ -816,6 +815,16 @@ defmodule Req.Steps do ## Request Options * `:finch` - the name of the Finch pool. Defaults to a pool automatically started by Req. + Can be either a pool name (atom) or a `{name, opts}` tuple where `opts` can include: + + * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. + Defaults to `:default`. This allows routing requests to different pools for the same + host. See `Finch.Pool.new/2` for more information on configuring tagged pools. + + Examples: + + Req.get!("https://api.example.com/data", finch: MyFinch) + Req.get!("https://api.example.com/data", finch: {MyFinch, pool_tag: :bulk}) * `:connect_options` - dynamically starts (or re-uses already started) Finch pool with the given connection options: @@ -853,10 +862,6 @@ defmodule Req.Steps do * `:unix_socket` - if set, connect through the given UNIX domain socket. - * `:pool_tag` - the tag to use when selecting which Finch pool to use for a request. - Defaults to `:default`. This allows routing requests to different pools for the same - host. See `Finch.Pool.new/2` for more information on configuring tagged pools. - * `:pool_max_idle_time` - the maximum number of milliseconds that a pool can be idle before being terminated, used only by HTTP1 pools. Default to `:infinity`. diff --git a/test/req/finch_test.exs b/test/req/finch_test.exs index 65de4577..5b547175 100644 --- a/test/req/finch_test.exs +++ b/test/req/finch_test.exs @@ -665,7 +665,7 @@ defmodule Req.FinchTest do {req, Req.Response.new(status: resp.status, headers: resp.headers, body: resp.body)} end - assert Req.get!(url, finch_request: fun, pool_tag: :bulk).body == "ok" + assert Req.get!(url, finch_request: fun, finch: {Req.Finch, pool_tag: :bulk}).body == "ok" assert_received {:pool_tag, :bulk} end