fix(replicas): parse Prisma DATABASE_URL and dump tables by name
Passwords with @ broke libpq URIs; convert to keyword conninfo. pg_dump -t public."table" produced empty dumps so replicas stayed at 0. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,7 +10,6 @@ postgresql://USER:PASSWORD@SLAVE_2_IP:5432/superadmin"
|
||||
# Read-only app user on replicas (program-settings API). Grants re-applied after data repair.
|
||||
# REPLICA_APP_DB_USER=montime
|
||||
|
||||
# Publisher DB for pg_dump / count checks. Required when DATABASE_URL password has @
|
||||
# (Prisma tolerates it; libpq URI parsing breaks → host "Montime@127.0.0.1").
|
||||
# Use postgres superuser on localhost; URL-encode special chars (@ → %40).
|
||||
# PUBLISHER_DSN="postgresql://postgres:PASS@127.0.0.1:5432/superadmin"
|
||||
# Publisher DB. Prefer this if DATABASE_URL password contains @ (must be URL-encoded here).
|
||||
# Master on montech-01 listens on 15432:
|
||||
# PUBLISHER_DSN="postgresql://montime:09128081392%40Montime@127.0.0.1:15432/superadmin"
|
||||
|
||||
+57
-16
@@ -45,8 +45,24 @@ if [[ -z "${DATABASE_URL:-}" && -f "$API_DIR/.env" ]]; then
|
||||
set -a && source "$API_DIR/.env" && set +a
|
||||
fi
|
||||
|
||||
# Prisma DATABASE_URL often has @ in the password (e.g. Admin@12345) which breaks
|
||||
# libpq URI parsing → host "Montime@127.0.0.1". Use an explicit publisher DSN.
|
||||
# Prisma DATABASE_URL often has @ in the password (09128081392@Montime).
|
||||
# libpq URI parsing then treats host as "Montime@127.0.0.1". Convert to keyword form.
|
||||
uri_to_conninfo() {
|
||||
local u="${1%%\?*}"
|
||||
if [[ "$u" != postgresql://* && "$u" != postgres://* ]]; then
|
||||
echo "$u"
|
||||
return 0
|
||||
fi
|
||||
local rest="${u#*://}"
|
||||
if [[ "$rest" =~ ^([^:]+):(.+)@([^:/]+):([0-9]+)/([^/?]+) ]]; then
|
||||
printf 'host=%s port=%s dbname=%s user=%s password=%s' \
|
||||
"${BASH_REMATCH[3]}" "${BASH_REMATCH[4]}" "${BASH_REMATCH[5]}" \
|
||||
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
|
||||
return 0
|
||||
fi
|
||||
echo "$u"
|
||||
}
|
||||
|
||||
resolve_publisher_dsn() {
|
||||
if [[ -n "${PUBLISHER_DSN:-}" ]]; then
|
||||
echo "$PUBLISHER_DSN"
|
||||
@@ -57,14 +73,10 @@ resolve_publisher_dsn() {
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${DATABASE_URL:-}" ]]; then
|
||||
local url="${DATABASE_URL%%\?*}"
|
||||
if [[ "$url" == postgresql://* || "$url" == postgres://* ]]; then
|
||||
echo "$url"
|
||||
return 0
|
||||
fi
|
||||
echo "$DATABASE_URL"
|
||||
return 0
|
||||
fi
|
||||
echo "Set PUBLISHER_DSN in deploy/replicas.env (postgresql://…@127.0.0.1:5432/superadmin)." >&2
|
||||
echo "Prisma DATABASE_URL alone is not reliable for pg_dump when the password contains @." >&2
|
||||
echo "Set DATABASE_URL or PUBLISHER_DSN (publisher Postgres)." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -79,8 +91,7 @@ run_psql() {
|
||||
}
|
||||
|
||||
libpq_dsn() {
|
||||
local u="$1"
|
||||
echo "${u%%\?*}"
|
||||
uri_to_conninfo "$1"
|
||||
}
|
||||
|
||||
label() {
|
||||
@@ -125,17 +136,45 @@ grant_app_user() {
|
||||
" 2>/dev/null || true
|
||||
}
|
||||
|
||||
grant_replica_writer() {
|
||||
local dsn="$1"
|
||||
local user=""
|
||||
if [[ "$dsn" == *"://"* ]]; then
|
||||
user="$(sed -E 's#^[a-z]+://([^:/]+).*#\1#' <<< "$dsn")"
|
||||
else
|
||||
user="$(sed -nE 's/.*user=([^ ]+).*/\1/p' <<< "$dsn")"
|
||||
fi
|
||||
[[ -n "$user" ]] || return 0
|
||||
echo " grant write to $user"
|
||||
run_psql "$(libpq_dsn "$dsn")" -v ON_ERROR_STOP=1 -q -c "
|
||||
GRANT USAGE ON SCHEMA public TO ${user};
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON ALL TABLES IN SCHEMA public TO ${user};
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${user};
|
||||
" || true
|
||||
}
|
||||
|
||||
copy_table_from_publisher() {
|
||||
local replica_dsn="$1" table="$2"
|
||||
local pub_dsn
|
||||
pub_dsn="$(libpq_dsn "$PUBLISHER")"
|
||||
pg_dump "$pub_dsn" \
|
||||
-t "public.\"$table\"" \
|
||||
local dump
|
||||
dump="$(mktemp)"
|
||||
pg_dump "$(libpq_dsn "$PUBLISHER")" \
|
||||
-t "$table" \
|
||||
--data-only \
|
||||
--no-owner \
|
||||
--no-privileges \
|
||||
--column-inserts \
|
||||
--disable-triggers \
|
||||
| run_psql "$(libpq_dsn "$replica_dsn")" -v ON_ERROR_STOP=1 -q
|
||||
>"$dump"
|
||||
local bytes
|
||||
bytes="$(wc -c <"$dump" | tr -d ' ')"
|
||||
echo " dumped $table (${bytes} bytes)"
|
||||
if [[ "${bytes:-0}" -lt 20 ]]; then
|
||||
echo " ! pg_dump produced empty output for $table" >&2
|
||||
rm -f "$dump"
|
||||
return 1
|
||||
fi
|
||||
run_psql "$(libpq_dsn "$replica_dsn")" -v ON_ERROR_STOP=1 -f "$dump"
|
||||
rm -f "$dump"
|
||||
}
|
||||
|
||||
repair_replica() {
|
||||
@@ -176,6 +215,8 @@ repair_replica() {
|
||||
-c "ALTER SUBSCRIPTION \"${sub}\" DISABLE"
|
||||
done
|
||||
|
||||
grant_replica_writer "$replica_dsn"
|
||||
|
||||
for table in "${drifted[@]}"; do
|
||||
echo " truncating + copying $table"
|
||||
run_psql "$(libpq_dsn "$replica_dsn")" -v ON_ERROR_STOP=1 -q \
|
||||
|
||||
Reference in New Issue
Block a user