prop/lib/microwaveprop/workers/terrain_profile_worker.ex
Graham McIntire 4468fb3b61
Increase Oban retry attempts and backoff for external API workers
20 attempts with exponential backoff capped at 6 hours ensures
data is eventually fetched even during extended rate limiting or
API outages (~3 days of retrying before giving up).
2026-03-29 17:03:56 -05:00

62 lines
1.8 KiB
Elixir

defmodule Microwaveprop.Workers.TerrainProfileWorker do
@moduledoc false
use Oban.Worker, queue: :terrain, max_attempts: 20
alias Microwaveprop.Radio
alias Microwaveprop.Terrain
alias Microwaveprop.Terrain.ElevationClient
alias Microwaveprop.Terrain.TerrainAnalysis
@impl Oban.Worker
def backoff(%Oban.Job{attempt: attempt}) do
min(120 * Integer.pow(2, attempt - 1), _six_hours = 21_600)
end
@impl Oban.Worker
def perform(%Oban.Job{args: %{"qso_id" => qso_id}}) do
if Terrain.has_terrain_profile?(qso_id) do
:ok
else
qso = Radio.get_qso!(qso_id)
lat1 = qso.pos1["lat"]
lon1 = qso.pos1["lon"] || qso.pos1["lng"]
lat2 = qso.pos2["lat"]
lon2 = qso.pos2["lon"] || qso.pos2["lng"]
dist_km = Decimal.to_float(qso.distance_km)
freq_ghz = Decimal.to_float(qso.band) / 1000
case ElevationClient.fetch_elevation_profile(lat1, lon1, lat2, lon2) do
{:ok, profile} ->
analysis = TerrainAnalysis.analyse(profile, dist_km, freq_ghz)
path_points =
Enum.map(profile, fn p ->
%{
"lat" => p.lat,
"lon" => p.lon,
"d" => p.d,
"elev" => p.elev,
"dist_km" => p.dist_km
}
end)
Terrain.upsert_terrain_profile(%{
qso_id: qso_id,
sample_count: length(profile),
path_points: path_points,
max_elevation_m: analysis.max_elevation_m,
min_clearance_m: analysis.min_clearance_m,
diffraction_db: analysis.diffraction_db,
fresnel_hit_count: analysis.fresnel_hit_count,
obstructed_count: analysis.obstructed_count,
verdict: analysis.verdict
})
:ok
{:error, reason} ->
{:error, reason}
end
end
end
end