HTTP call sites: what hackney 4 changed under them

797496d · Gabriel Morell · 2026-10-23 15:09

12 files +22 -3
Message
{commit_body(@commit)}

Files changed

modified apps/room_gtfs/lib/room_gtfs/application.ex
+7 −0
@@ -45,6 +45,13 @@ defmodule RoomGtfs.Application do
45 45 # unbounded, a scheduled refresh of every source at once would try to hold
46 46 # them all.
47 47 #
48 + # On hackney 4 `max_connections` is how many idle connections each pool
49 + # keeps, not a cap on how many are open at once: the four-at-a-time limit is
50 + # `max_per_host` in the static worker's request options, and it holds per
51 + # host rather than across all of them. TLS connections are not pooled at
52 + # all unless asked for, so for https feeds these pools hold nothing between
53 + # requests.
54 + #
48 55 # Separate from hackney's default pool for the same reason. Everything else
49 56 # in this umbrella -- realtime feeds, GitHub, the weather -- shares that one,
50 57 # and a bulk transfer sitting in it starves requests that take milliseconds.
modified apps/room_gtfs/lib/room_gtfs/feed_health.ex
+4 −1
@@ -161,7 +161,10 @@ defmodule RoomGtfs.FeedHealth do
161 161 defp describe(:bad_status), do: "bad_status"
162 162 defp describe(:decode_failed), do: "decode_failed"
163 163 defp describe(:not_protobuf), do: "not_protobuf"
164 − defp describe(%HTTPoison.Error{reason: reason}), do: "http_#{reason}"
164 + # hackney 4 also answers with tuples ({:closed, _}, TLS alerts) and strings
165 + # (a malformed URL), which neither interpolate nor make a closed set.
166 + defp describe(%HTTPoison.Error{reason: reason}) when is_atom(reason), do: "http_#{reason}"
167 + defp describe(%HTTPoison.Error{}), do: "http_error"
165 168 defp describe({:fetch_crashed, _reason}), do: "crashed"
166 169 defp describe(reason) when is_atom(reason), do: Atom.to_string(reason)
167 170 defp describe(_reason), do: "unknown"
modified apps/room_gtfs/lib/room_gtfs/gtfs_worker.ex
+5 −1
@@ -1722,11 +1722,15 @@ defmodule RoomGtfs.Worker.Static do
1722 1722 # That is what the `:checkout_timeout`s on unrelated requests were: not those
1723 1723 # services being slow, but this download holding the pool. A pool of its own
1724 1724 # means a bulk transfer can only ever starve another bulk transfer.
1725 + #
1726 + # Since hackney 2 a pool's `max_connections` only sizes the idle connections
1727 + # it keeps, and does not limit how many are open; `max_per_host` does, one
1728 + # host at a time. Four per host is what the pool used to mean.
1725 1729 @static_http [
1726 1730 follow_redirect: true,
1727 1731 recv_timeout: 300_000,
1728 1732 timeout: 30_000,
1729 − hackney: [pool: :gtfs_static]
1733 + hackney: [pool: :gtfs_static, max_per_host: 4]
1730 1734 ]
1731 1735 use GenServer
1732 1736 require Logger
modified apps/room_sanctum/lib/room_sanctum/workers/ankyra_worker.ex
+6 −1
@@ -493,7 +493,12 @@ defmodule RoomSanctum.Worker.Ankyra do
493 493 HTTPoison.get(
494 494 String.trim_trailing(url, "/") <> "/api/connections?columns=protocol,user,vhost,client_properties.client_id",
495 495 [{"accept", "application/json"}],
496 − hackney: [basic_auth: {to_string(cfg[:username]), to_string(cfg[:password])}],
496 + # The management API is reached over plain http inside the deployment.
497 + # hackney 4 can refuse basic auth over http unless told it is meant.
498 + hackney: [
499 + basic_auth: {to_string(cfg[:username]), to_string(cfg[:password])},
500 + insecure_basic_auth: true
501 + ],
497 502 timeout: 2_000,
498 503 recv_timeout: 2_000
499 504 )

Parents: f8e0641