perf(rover): tighten Overpass timeout and log per-step compute timing

Calculate occasionally hung in production for ~30s. Adds per-phase
timing logs to Compute.run so the slow step is visible in pod logs,
and trims the Overpass road-proximity request to a 10s server-side
timeout / 12s receive timeout (no Req retries) so a slow road API
fails soft to no road penalty instead of stalling Calculate.
This commit is contained in:
Graham McIntire 2026-04-25 18:05:28 -05:00
parent 10cbd8dd48
commit 4bfcff4e32
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
2 changed files with 25 additions and 9 deletions

View file

@ -17,6 +17,8 @@ defmodule Microwaveprop.Rover.Compute do
alias Microwaveprop.Rover.Prominence
alias Microwaveprop.Rover.RoadProximity
require Logger
@avg_speed_kmh 65.0
@drive_penalty_db_per_hour 2.0
@top_n 5
@ -86,23 +88,25 @@ defmodule Microwaveprop.Rover.Compute do
radius_km = max_distance_km * 1.0
bbox = bbox_around(home, radius_km)
raw_cells = scores_at.(band_mhz, valid_time, bbox)
raw_cells = time_step("scores_at", fn -> scores_at.(band_mhz, valid_time, bbox) end)
# First filter: drive radius
in_radius =
Enum.filter(raw_cells, fn cell ->
DriveTime.haversine_km({home.lat, home.lon}, {cell.lat, cell.lon}) <= radius_km
end)
# Bulk SRTM lookup for cell centers, then per-link path-terrain clearance.
Logger.info(
"rover compute: radius=#{radius_km}km cells=#{length(raw_cells)} in_radius=#{length(in_radius)} stations=#{length(selected_stations)}"
)
points = Enum.map(in_radius, &{&1.lat, &1.lon})
elev_map = elev_lookup.(points)
clearance_map = clearance_lookup.(in_radius, selected_stations)
prominence_map = prominence_lookup.(in_radius)
elev_map = time_step("elev_lookup", fn -> elev_lookup.(points) end)
clearance_map = time_step("clearance", fn -> clearance_lookup.(in_radius, selected_stations) end)
prominence_map = time_step("prominence", fn -> prominence_lookup.(in_radius) end)
road_map =
if Application.get_env(:microwaveprop, :rover_road_proximity_enabled, true),
do: fetch_road_map(road_lookup, in_radius, bbox),
do: time_step("road_proximity", fn -> fetch_road_map(road_lookup, in_radius, bbox) end),
else: %{}
home_elev = home.elev_m || 0
@ -203,6 +207,12 @@ defmodule Microwaveprop.Rover.Compute do
end
end
defp time_step(label, fun) do
{us, result} = :timer.tc(fun)
Logger.info("rover compute: #{label} took #{div(us, 1000)} ms")
result
end
defp fetch_road_map(road_lookup, cells, bbox) do
case road_lookup.(cells, bbox) do
{:ok, map} -> map

View file

@ -16,6 +16,8 @@ defmodule Microwaveprop.Rover.RoadProximity do
@overpass_url "https://overpass-api.de/api/interpreter"
@road_types ~w(motorway trunk primary secondary tertiary unclassified residential)
@overpass_query_timeout_s 10
@overpass_receive_timeout_ms 12_000
@type latlon :: {float(), float()}
@type segment :: {latlon(), latlon()}
@ -44,12 +46,16 @@ defmodule Microwaveprop.Rover.RoadProximity do
types = Enum.join(@road_types, "|")
query = """
[out:json][timeout:25];
[out:json][timeout:#{@overpass_query_timeout_s}];
way["highway"~"^(#{types})$"](#{s},#{w},#{n},#{e});
out geom;
"""
case Req.post(@overpass_url, form: [data: query], receive_timeout: 30_000) do
case Req.post(@overpass_url,
form: [data: query],
receive_timeout: @overpass_receive_timeout_ms,
retry: false
) do
{:ok, %{status: 200, body: %{"elements" => elements}}} ->
{:ok, segments_from_elements(elements)}