The hourly cron now only seeds grid_tasks. The chain step, native-duct
merge, NEXRAD merge, commercial-link merge, scoring, ProfilesFile
write, and band-score writes all moved to rust/prop_grid_rs.
Elixir changes:
- GridTaskEnqueuer.seed_with_analysis/1: inserts 1 kind='analysis' row
(f00) + 18 kind='forecast' rows (f01..f18).
- PropagationGridWorker: stripped from 423 LOC to a thin seeder.
perform(%{}) → GridTaskEnqueuer.seed_with_analysis.
Deleted: process_forecast_hour, merge_native_duct_data,
merge_nexrad_data, merge_commercial_link_data, compute_scores_*,
persist_profiles, record_run_timing (Rust emits spans to Prometheus
instead), apply_nexrad_observations, apply_duct_grid, timed helpers.
Test rewritten for the new shape: 0 Oban fan-out jobs, 19 grid_tasks
rows with the expected kind distribution.
HrrrNativeClient and NexradClient remain — they have other callers
(HrrrNativeGridWorker for per-QSO duct batch; NexradWorker and
CommonVolumeRadarWorker for per-contact radar). Only f00's direct
use moved.
115 lines
3.2 KiB
Elixir
115 lines
3.2 KiB
Elixir
defmodule Microwaveprop.Propagation.GridTaskEnqueuer do
|
|
@moduledoc """
|
|
Seeds `grid_tasks` rows for the Rust `prop-grid-rs` worker.
|
|
|
|
Called from `PropagationGridWorker.seed_chain/0`. Rust claims
|
|
kind='forecast' and kind='analysis' rows, with analysis lanes
|
|
taking priority (see `claim_next_analysis` in the Rust db module).
|
|
|
|
Inserts are idempotent via the `(run_time, forecast_hour, kind)`
|
|
unique index — re-seeding the same cycle is a no-op.
|
|
"""
|
|
|
|
alias Microwaveprop.Repo
|
|
|
|
require Logger
|
|
|
|
@max_forecast_hour 18
|
|
|
|
@doc """
|
|
Seed one kind='analysis' row (f00) plus 18 kind='forecast' rows
|
|
(f01..f18) for `run_time`. This is the Phase 3 Stream A cutover
|
|
shape: Rust owns the entire chain end-to-end.
|
|
"""
|
|
@spec seed_with_analysis(DateTime.t()) :: {:ok, non_neg_integer()} | {:error, term()}
|
|
def seed_with_analysis(%DateTime{} = run_time) do
|
|
run_time = DateTime.truncate(run_time, :second)
|
|
now = DateTime.truncate(DateTime.utc_now(), :microsecond)
|
|
|
|
analysis_row = %{
|
|
id: Ecto.UUID.bingenerate(),
|
|
run_time: run_time,
|
|
forecast_hour: 0,
|
|
valid_time: run_time,
|
|
status: "queued",
|
|
attempt: 0,
|
|
kind: "analysis",
|
|
claimed_at: nil,
|
|
completed_at: nil,
|
|
error: nil,
|
|
inserted_at: now,
|
|
updated_at: now
|
|
}
|
|
|
|
forecast_rows =
|
|
for fh <- 1..@max_forecast_hour do
|
|
%{
|
|
id: Ecto.UUID.bingenerate(),
|
|
run_time: run_time,
|
|
forecast_hour: fh,
|
|
valid_time: DateTime.add(run_time, fh * 3600, :second),
|
|
status: "queued",
|
|
attempt: 0,
|
|
kind: "forecast",
|
|
claimed_at: nil,
|
|
completed_at: nil,
|
|
error: nil,
|
|
inserted_at: now,
|
|
updated_at: now
|
|
}
|
|
end
|
|
|
|
do_insert([analysis_row | forecast_rows], run_time)
|
|
end
|
|
|
|
@doc """
|
|
Legacy: seed only kind='forecast' rows (f01..f18). Retained for
|
|
tooling that needs to re-seed the forecast lane without touching
|
|
the analysis row. `seed_with_analysis/1` is the production path.
|
|
"""
|
|
@spec seed(DateTime.t()) :: {:ok, non_neg_integer()} | {:error, term()}
|
|
def seed(%DateTime{} = run_time) do
|
|
run_time = DateTime.truncate(run_time, :second)
|
|
now = DateTime.truncate(DateTime.utc_now(), :microsecond)
|
|
|
|
rows =
|
|
for fh <- 1..@max_forecast_hour do
|
|
%{
|
|
id: Ecto.UUID.bingenerate(),
|
|
run_time: run_time,
|
|
forecast_hour: fh,
|
|
valid_time: DateTime.add(run_time, fh * 3600, :second),
|
|
status: "queued",
|
|
attempt: 0,
|
|
kind: "forecast",
|
|
claimed_at: nil,
|
|
completed_at: nil,
|
|
error: nil,
|
|
inserted_at: now,
|
|
updated_at: now
|
|
}
|
|
end
|
|
|
|
do_insert(rows, run_time)
|
|
end
|
|
|
|
defp do_insert(rows, run_time) do
|
|
{count, _} =
|
|
Repo.insert_all("grid_tasks", rows,
|
|
on_conflict: :nothing,
|
|
conflict_target: [:run_time, :forecast_hour, :kind]
|
|
)
|
|
|
|
if count > 0 do
|
|
Logger.info("GridTaskEnqueuer: seeded #{count} grid_tasks for run_time=#{run_time}")
|
|
else
|
|
Logger.info("GridTaskEnqueuer: run_time=#{run_time} already seeded")
|
|
end
|
|
|
|
{:ok, count}
|
|
rescue
|
|
e ->
|
|
Logger.error("GridTaskEnqueuer: seed failed: #{inspect(e)}")
|
|
{:error, e}
|
|
end
|
|
end
|