From 9cce257db78d9839bee9fb1d509bcf11ec8ebe8a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Mon, 1 Jun 2026 15:32:36 -0500 Subject: [PATCH] perf: fix medium-severity N+1 and inefficiency patterns - pending_edit_for_user: add preload for [:user, :contact] to avoid lazy-loaded N+1 when templates access those associations - ScoresController.encode_binary: combine 3 Enum.map passes into a single Enum.reduce, halving list traversals for score binary encoding - find_duplicate_contact: push station-pair/grid matching into SQL WHERE clause so dedup uses the DB index instead of loading all matching rows + Enum.find in Elixir --- lib/microwaveprop/radio.ex | 20 +++++++------------ .../controllers/scores_controller.ex | 9 ++++++--- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/microwaveprop/radio.ex b/lib/microwaveprop/radio.ex index 3e1a50f3..b3c9c5ef 100644 --- a/lib/microwaveprop/radio.ex +++ b/lib/microwaveprop/radio.ex @@ -757,25 +757,18 @@ defmodule Microwaveprop.Radio do min_ts = DateTime.add(ts, -@dedup_window_seconds, :second) max_ts = DateTime.add(ts, @dedup_window_seconds, :second) - expected_pair = Enum.sort([{s1, g1}, {s2, g2}]) - from(c in Contact, where: c.band == ^band_decimal and c.qso_timestamp >= ^min_ts and c.qso_timestamp <= ^max_ts and - c.flagged_invalid == false + c.flagged_invalid == false and + fragment("LEAST(UPPER(station1), UPPER(station2)) = ?", ^min(s1, s2)) and + fragment("GREATEST(UPPER(station1), UPPER(station2)) = ?", ^max(s1, s2)) and + fragment("LEAST(UPPER(grid1), UPPER(grid2)) = ?", ^min(g1, g2)) and + fragment("GREATEST(UPPER(grid1), UPPER(grid2)) = ?", ^max(g1, g2)) ) - |> Repo.all() - |> Enum.find(fn c -> - pair = - Enum.sort([ - {String.upcase(c.station1), String.upcase(c.grid1)}, - {String.upcase(c.station2), String.upcase(c.grid2)} - ]) - - pair == expected_pair - end) + |> Repo.one() end @refinement_fields ~w(grid1 grid2 mode)a @@ -1101,6 +1094,7 @@ defmodule Microwaveprop.Radio do def pending_edit_for_user(contact_id, user_id) do ContactEdit |> where([e], e.contact_id == ^contact_id and e.user_id == ^user_id and e.status == :pending) + |> preload([:user, :contact]) |> Repo.one() end diff --git a/lib/microwaveprop_web/controllers/scores_controller.ex b/lib/microwaveprop_web/controllers/scores_controller.ex index 5aa95aa0..09095108 100644 --- a/lib/microwaveprop_web/controllers/scores_controller.ex +++ b/lib/microwaveprop_web/controllers/scores_controller.ex @@ -103,9 +103,12 @@ defmodule MicrowavepropWeb.ScoresController do header = <<"PSCR", 1::8, 0::8, 0::8, 0::8, cell_count::little-32>> - lats = scores |> Enum.map(&<<&1.lat * 1.0::little-float-32>>) |> IO.iodata_to_binary() - lons = scores |> Enum.map(&<<&1.lon * 1.0::little-float-32>>) |> IO.iodata_to_binary() - score_bytes = scores |> Enum.map(&<>) |> IO.iodata_to_binary() + {lats, lons, score_bytes} = + Enum.reduce(scores, {[], [], []}, fn s, {lats_acc, lons_acc, scs_acc} -> + {[<> | lats_acc], + [<> | lons_acc], + [<> | scs_acc]} + end) <> end