Stage 2 of the HRDPS plan. Mirror of hrrr_profiles for HRDPS-derived
rows so HRDPS can be dropped/reprocessed without disturbing the 4500+
HRRR profiles already in prod. Same column set, same indexes,
PARTITION BY RANGE (valid_time) with quarterly partitions starting at
the deploy quarter.
Schema follows the @primary_key {:id, :binary_id, autogenerate: true}
convention and the same set of derived fields HrrrProfile carries
(surface_refractivity, min_refractivity_gradient, ducting_detected,
duct_characteristics, etc.) so the rest of the pipeline can consume
HRDPS-derived rows interchangeably with HRRR-derived rows.
Unique constraint on (lat, lon, valid_time) is enforced at the DB
level via the partitioned index; schema-level unique_constraint/2
matching against changeset errors doesn't apply because the
partition's index name varies per partition. Production upserts go
through `on_conflict: {:replace_all_except, ...}` per project
convention.
66 lines
2.4 KiB
Elixir
66 lines
2.4 KiB
Elixir
defmodule Microwaveprop.Repo.Migrations.CreateHrdpsProfiles do
|
|
use Ecto.Migration
|
|
|
|
@disable_ddl_transaction true
|
|
@disable_migration_lock true
|
|
|
|
# Mirror of hrrr_profiles for HRDPS-derived rows. Separate table (not a
|
|
# unified nwp_profiles) so HRDPS can be dropped/reprocessed without
|
|
# disturbing the 4500+ HRRR profiles already in prod. Same column set
|
|
# and indexes; PARTITION BY RANGE (valid_time) with quarterly partitions
|
|
# starting from the deploy quarter.
|
|
#
|
|
# Unlike hrrr_profiles, there's no historical archive to backfill into
|
|
# — HRDPS is forward-only — so the partition list starts at the deploy
|
|
# quarter and walks forward.
|
|
def up do
|
|
execute """
|
|
CREATE TABLE hrdps_profiles (
|
|
id uuid NOT NULL DEFAULT gen_random_uuid(),
|
|
valid_time timestamp(0) WITHOUT TIME ZONE NOT NULL,
|
|
lat double precision NOT NULL,
|
|
lon double precision NOT NULL,
|
|
run_time timestamp(0) WITHOUT TIME ZONE,
|
|
profile jsonb[] DEFAULT ARRAY[]::jsonb[],
|
|
hpbl_m double precision,
|
|
pwat_mm double precision,
|
|
surface_temp_c double precision,
|
|
surface_dewpoint_c double precision,
|
|
surface_pressure_mb double precision,
|
|
surface_refractivity double precision,
|
|
min_refractivity_gradient double precision,
|
|
ducting_detected boolean DEFAULT false,
|
|
duct_characteristics jsonb[],
|
|
is_grid_point boolean DEFAULT false,
|
|
inserted_at timestamp(0) WITHOUT TIME ZONE NOT NULL,
|
|
updated_at timestamp(0) WITHOUT TIME ZONE NOT NULL
|
|
) PARTITION BY RANGE (valid_time)
|
|
"""
|
|
|
|
partitions = [
|
|
{"2026-04-01", "2026-07-01"},
|
|
{"2026-07-01", "2026-10-01"},
|
|
{"2026-10-01", "2027-01-01"},
|
|
{"2027-01-01", "2027-04-01"},
|
|
{"2027-04-01", "2027-07-01"},
|
|
{"2027-07-01", "2027-10-01"},
|
|
{"2027-10-01", "2028-01-01"}
|
|
]
|
|
|
|
for {from_date, to_date} <- partitions do
|
|
suffix = from_date |> String.replace("-", "_") |> String.slice(0..6)
|
|
|
|
execute """
|
|
CREATE TABLE hrdps_profiles_#{suffix} PARTITION OF hrdps_profiles
|
|
FOR VALUES FROM ('#{from_date}') TO ('#{to_date}')
|
|
"""
|
|
end
|
|
|
|
execute "CREATE UNIQUE INDEX hrdps_profiles_lat_lon_valid_time_index ON hrdps_profiles (lat, lon, valid_time)"
|
|
execute "CREATE INDEX hrdps_profiles_valid_time_index ON hrdps_profiles (valid_time)"
|
|
end
|
|
|
|
def down do
|
|
execute "DROP TABLE IF EXISTS hrdps_profiles CASCADE"
|
|
end
|
|
end
|