perf(rover-planning): create_mission returns immediately, batch path / Oban inserts
Two stacked wins for the form-submit hot path: 1) create_mission now hands path-matrix population to the same async reconcile worker update_mission uses, so the request returns right after the Mission row insert. For a mission with hundreds of (rover x station x band) tuples this used to block the LiveView for many seconds. 2) Inside reconcile/enqueue_paths_for, the per-pair Multi.insert loop is replaced with one Repo.insert_all for the Path rows and one Oban.insert_all for the worker jobs. N round-trips collapse to two regardless of pair count.
This commit is contained in:
parent
a4f0e171e8
commit
3060eddfb2
1 changed files with 36 additions and 26 deletions
|
|
@ -13,7 +13,6 @@ defmodule Microwaveprop.RoverPlanning do
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Ecto.Multi
|
|
||||||
alias Microwaveprop.Accounts.User
|
alias Microwaveprop.Accounts.User
|
||||||
alias Microwaveprop.Repo
|
alias Microwaveprop.Repo
|
||||||
alias Microwaveprop.Rover.Location
|
alias Microwaveprop.Rover.Location
|
||||||
|
|
@ -60,7 +59,13 @@ defmodule Microwaveprop.RoverPlanning do
|
||||||
case Repo.insert(changeset) do
|
case Repo.insert(changeset) do
|
||||||
{:ok, mission} ->
|
{:ok, mission} ->
|
||||||
mission = Repo.preload(mission, [:user, :stations])
|
mission = Repo.preload(mission, [:user, :stations])
|
||||||
:ok = enqueue_paths_for(mission)
|
# Async: a brand-new mission can have hundreds of (rover x
|
||||||
|
# station x band) tuples. Hand the matrix population off to
|
||||||
|
# the reconcile worker so the form-submit request returns
|
||||||
|
# immediately instead of blocking on N inserts + N Oban
|
||||||
|
# enqueues per pair. The worker calls reconcile_mission_paths,
|
||||||
|
# which is identical to enqueue_paths_for for an empty matrix.
|
||||||
|
:ok = enqueue_reconcile(mission)
|
||||||
{:ok, mission}
|
{:ok, mission}
|
||||||
|
|
||||||
{:error, _} = error ->
|
{:error, _} = error ->
|
||||||
|
|
@ -226,43 +231,48 @@ defmodule Microwaveprop.RoverPlanning do
|
||||||
defp insert_pending_paths_and_jobs([], _mission), do: :ok
|
defp insert_pending_paths_and_jobs([], _mission), do: :ok
|
||||||
|
|
||||||
defp insert_pending_paths_and_jobs(tuples, mission) when is_list(tuples) do
|
defp insert_pending_paths_and_jobs(tuples, mission) when is_list(tuples) do
|
||||||
{:ok, _} =
|
# Batch the path-row inserts and the Oban-job inserts into a
|
||||||
tuples
|
# single SQL statement each. The previous Multi-of-N-inserts +
|
||||||
|> Enum.reduce(Multi.new(), fn {rover_id, station_id, band_mhz}, multi ->
|
# N x Oban.insert call took ~1.5s per pair × 900 pairs in the
|
||||||
attrs = %{
|
# worst-case mission — `insert_all` collapses both to one
|
||||||
|
# roundtrip per kind regardless of pair count.
|
||||||
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||||
|
|
||||||
|
path_rows =
|
||||||
|
Enum.map(tuples, fn {rover_id, station_id, band_mhz} ->
|
||||||
|
%{
|
||||||
|
id: Ecto.UUID.generate(),
|
||||||
mission_id: mission.id,
|
mission_id: mission.id,
|
||||||
rover_location_id: rover_id,
|
rover_location_id: rover_id,
|
||||||
station_id: station_id,
|
station_id: station_id,
|
||||||
band_mhz: band_mhz,
|
band_mhz: band_mhz,
|
||||||
status: :pending
|
status: :pending,
|
||||||
|
inserted_at: now,
|
||||||
|
updated_at: now
|
||||||
}
|
}
|
||||||
|
|
||||||
Multi.insert(
|
|
||||||
multi,
|
|
||||||
{:path, rover_id, station_id, band_mhz},
|
|
||||||
Path.changeset(%Path{}, attrs),
|
|
||||||
on_conflict: :nothing,
|
|
||||||
conflict_target: [:mission_id, :rover_location_id, :station_id, :band_mhz]
|
|
||||||
)
|
|
||||||
end)
|
end)
|
||||||
|> Repo.transaction()
|
|
||||||
|
Repo.insert_all(Path, path_rows,
|
||||||
|
on_conflict: :nothing,
|
||||||
|
conflict_target: [:mission_id, :rover_location_id, :station_id, :band_mhz]
|
||||||
|
)
|
||||||
|
|
||||||
enqueue_jobs(tuples, mission)
|
enqueue_jobs(tuples, mission)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp enqueue_jobs(tuples, mission) do
|
defp enqueue_jobs(tuples, mission) do
|
||||||
for {rover_id, station_id, band_mhz} <- tuples do
|
changesets =
|
||||||
%{
|
Enum.map(tuples, fn {rover_id, station_id, band_mhz} ->
|
||||||
"mission_id" => mission.id,
|
RoverPathProfileWorker.new(%{
|
||||||
"rover_location_id" => rover_id,
|
"mission_id" => mission.id,
|
||||||
"station_id" => station_id,
|
"rover_location_id" => rover_id,
|
||||||
"band_mhz" => band_mhz
|
"station_id" => station_id,
|
||||||
}
|
"band_mhz" => band_mhz
|
||||||
|> RoverPathProfileWorker.new()
|
})
|
||||||
|> Oban.insert()
|
end)
|
||||||
end
|
|
||||||
|
|
||||||
|
Oban.insert_all(changesets)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue