diff --git a/config/dev.exs b/config/dev.exs index 9abd0579..6e28eda5 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -8,7 +8,7 @@ config :microwaveprop, Microwaveprop.Repo, username: "postgres", password: "postgres", hostname: "localhost", - database: "microwaveprop_dev", + database: "prop_dev", stacktrace: true, show_sensitive_data_on_connection_error: true, pool_size: 30 diff --git a/docs/plans/2026-04-13-rdps-vertical-profiles.md b/docs/plans/2026-04-13-rdps-vertical-profiles.md index 2003b71d..0fdb25eb 100644 --- a/docs/plans/2026-04-13-rdps-vertical-profiles.md +++ b/docs/plans/2026-04-13-rdps-vertical-profiles.md @@ -144,7 +144,7 @@ Copy shape from `lib/microwaveprop/weather/hrrr_profile.ex`, strip anything RDPS ```bash mix ecto.migrate -psql -h localhost -d microwaveprop_dev -c "\d rdps_profiles" +psql -h localhost -d prop_dev -c "\d rdps_profiles" ``` **Step 2.5: Run schema test, verify PASS** diff --git a/scripts/restore_prod_to_local.sh b/scripts/restore_prod_to_local.sh new file mode 100755 index 00000000..36210f43 --- /dev/null +++ b/scripts/restore_prod_to_local.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# +# Pull the latest production Postgres dump off the DB server and restore +# it into the local `prop_dev` database, replacing whatever is there. +# +# The production server runs scripts/backup_prop_db.sh on a daily cron, +# which writes pg_dump custom-format dumps to /backups/prop/ as +# `prop_YYYYMMDD_HHMMSS.dump`. We SSH in, pick the newest one, scp it +# locally, then drop/recreate prop_dev and restore into it. +# +# Usage: +# +# scripts/restore_prod_to_local.sh +# REMOTE_HOST=my-db-host REMOTE_USER=graham scripts/restore_prod_to_local.sh +# +# Before running: stop `mix phx.server` so Ecto doesn't hold connections. + +set -euo pipefail + +REMOTE_HOST="${REMOTE_HOST:-db-prop}" +REMOTE_USER="${REMOTE_USER:-$USER}" +REMOTE_BACKUP_DIR="${REMOTE_BACKUP_DIR:-/backups/prop}" +LOCAL_DB="${LOCAL_DB:-prop_dev}" +LOCAL_USER="${LOCAL_USER:-postgres}" +LOCAL_HOST="${LOCAL_HOST:-localhost}" +LOCAL_TMP="${LOCAL_TMP:-/tmp}" + +ssh_target="${REMOTE_USER}@${REMOTE_HOST}" + +log() { + printf '%s restore_prod_to_local: %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*" +} + +die() { + log "ERROR: $*" + exit 1 +} + +command -v ssh >/dev/null || die "ssh not on PATH" +command -v scp >/dev/null || die "scp not on PATH" +command -v pg_restore >/dev/null || die "pg_restore not on PATH (install postgresql client tools)" +command -v psql >/dev/null || die "psql not on PATH" + +log "looking up latest dump on ${ssh_target}:${REMOTE_BACKUP_DIR}" +remote_file=$(ssh "$ssh_target" "ls -1t ${REMOTE_BACKUP_DIR}/prop_*.dump 2>/dev/null | head -n1") +[[ -n "$remote_file" ]] || die "no prop_*.dump files found in ${REMOTE_BACKUP_DIR} on ${REMOTE_HOST}" + +filename=$(basename "$remote_file") +local_file="${LOCAL_TMP}/${filename}" +log "latest dump: ${remote_file}" + +# Always remove the downloaded dump, even on mid-run failure. Saves disk +# on laptops where dumps are multi-GB. +cleanup() { + [[ -n "${local_file:-}" && -f "$local_file" ]] && rm -f "$local_file" +} +trap cleanup EXIT + +log "downloading to ${local_file}" +scp "${ssh_target}:${remote_file}" "$local_file" + +# Kick any existing connections off the DB before dropping it — otherwise +# `DROP DATABASE` fails with "is being accessed by other users". This is +# aggressive, but it's a dev box and the user was told to stop the server. +log "terminating existing connections to ${LOCAL_DB}" +psql -h "$LOCAL_HOST" -U "$LOCAL_USER" -d postgres -v ON_ERROR_STOP=1 </dev/null +SELECT pg_terminate_backend(pid) +FROM pg_stat_activity +WHERE datname = '${LOCAL_DB}' AND pid <> pg_backend_pid(); +SQL + +log "dropping and recreating ${LOCAL_DB}" +psql -h "$LOCAL_HOST" -U "$LOCAL_USER" -d postgres -v ON_ERROR_STOP=1 </dev/null +DROP DATABASE IF EXISTS ${LOCAL_DB}; +CREATE DATABASE ${LOCAL_DB} OWNER ${LOCAL_USER}; +SQL + +# --no-owner / --no-acl so ownership in the prod dump doesn't cause role +# errors locally. -j 4 parallelises table data restore. +log "restoring dump into ${LOCAL_DB}" +pg_restore \ + -h "$LOCAL_HOST" \ + -U "$LOCAL_USER" \ + -d "$LOCAL_DB" \ + --no-owner \ + --no-acl \ + -j 4 \ + "$local_file" + +log "done — ${LOCAL_DB} is a copy of prod as of $(basename "$filename" .dump)"