60 lines
1.5 KiB
Bash
Executable File
60 lines
1.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Sync PostgreSQL from SOURCE_DB_URL -> DEST_DB_URL.
|
|
# Source is read-only: only pg_dump (SELECT) with default_transaction_read_only=on.
|
|
set -euo pipefail
|
|
|
|
log() {
|
|
echo "[$(date -Iseconds)] $*" >&2
|
|
}
|
|
|
|
SOURCE_DB_URL="${SOURCE_DB_URL:-}"
|
|
DEST_DB_URL="${DEST_DB_URL:-}"
|
|
|
|
if [ -z "$SOURCE_DB_URL" ]; then
|
|
log "ERROR: SOURCE_DB_URL must be set"
|
|
exit 1
|
|
fi
|
|
if [ -z "$DEST_DB_URL" ]; then
|
|
log "ERROR: DEST_DB_URL must be set"
|
|
exit 1
|
|
fi
|
|
|
|
DUMP_FILE="${DUMP_FILE:-/tmp/db-sync.dump}"
|
|
# Extra safety: force read-only transactions on the source connection.
|
|
export PGOPTIONS_SOURCE="${PGOPTIONS_SOURCE:--c default_transaction_read_only=on}"
|
|
|
|
log "Starting sync (source -> dest)"
|
|
log "Dumping source (read-only)..."
|
|
|
|
# pg_dump only reads from source. --clean/--if-exists apply DROP statements to DEST on restore.
|
|
PGOPTIONS="$PGOPTIONS_SOURCE" pg_dump \
|
|
--format=custom \
|
|
--no-owner \
|
|
--no-acl \
|
|
--verbose \
|
|
--file="$DUMP_FILE" \
|
|
"$SOURCE_DB_URL"
|
|
|
|
log "Restoring into destination (with --clean)..."
|
|
pg_restore \
|
|
--clean \
|
|
--if-exists \
|
|
--no-owner \
|
|
--no-acl \
|
|
--verbose \
|
|
--dbname="$DEST_DB_URL" \
|
|
"$DUMP_FILE" || {
|
|
# pg_restore returns non-zero on some non-fatal warnings (e.g. missing roles).
|
|
# Treat exit code 1 as warning; fail hard on higher codes.
|
|
status=$?
|
|
if [ "$status" -gt 1 ]; then
|
|
log "ERROR: pg_restore failed with exit code $status"
|
|
rm -f "$DUMP_FILE"
|
|
exit "$status"
|
|
fi
|
|
log "WARNING: pg_restore finished with warnings (exit $status)"
|
|
}
|
|
|
|
rm -f "$DUMP_FILE"
|
|
log "Sync completed successfully"
|