diff --git a/lib/microwaveprop/propagation.ex b/lib/microwaveprop/propagation.ex index 5f15573a..13ae877c 100644 --- a/lib/microwaveprop/propagation.ex +++ b/lib/microwaveprop/propagation.ex @@ -129,7 +129,17 @@ defmodule Microwaveprop.Propagation do |> Enum.reduce(0, fn chunk, acc -> {count, _} = Repo.insert_all(GridScore, chunk, - on_conflict: {:replace, [:score, :factors, :updated_at]}, + on_conflict: + from(g in GridScore, + update: [ + set: [ + score: fragment("EXCLUDED.score"), + factors: fragment("EXCLUDED.factors"), + updated_at: fragment("EXCLUDED.updated_at") + ] + ], + where: g.score != fragment("EXCLUDED.score") + ), conflict_target: [:lat, :lon, :valid_time, :band_mhz] ) diff --git a/priv/repo/migrations/20260404145255_tune_propagation_scores_autovacuum.exs b/priv/repo/migrations/20260404145255_tune_propagation_scores_autovacuum.exs new file mode 100644 index 00000000..fa9b8cd7 --- /dev/null +++ b/priv/repo/migrations/20260404145255_tune_propagation_scores_autovacuum.exs @@ -0,0 +1,33 @@ +defmodule Microwaveprop.Repo.Migrations.TunePropagationScoresAutovacuum do + use Ecto.Migration + + def up do + # Aggressive autovacuum for propagation_scores — high churn table with + # ~14M upserts per hourly run. Default settings can't keep up, causing + # 100M+ dead tuples and 70GB+ bloat. + execute """ + ALTER TABLE propagation_scores SET ( + autovacuum_vacuum_scale_factor = 0.01, + autovacuum_vacuum_threshold = 10000, + autovacuum_vacuum_cost_delay = 0, + autovacuum_vacuum_cost_limit = 2000, + autovacuum_analyze_scale_factor = 0.02 + ) + """ + + # Also run ANALYZE to refresh stats after this change + execute "ANALYZE propagation_scores" + end + + def down do + execute """ + ALTER TABLE propagation_scores RESET ( + autovacuum_vacuum_scale_factor, + autovacuum_vacuum_threshold, + autovacuum_vacuum_cost_delay, + autovacuum_vacuum_cost_limit, + autovacuum_analyze_scale_factor + ) + """ + end +end