Files

202 lines
5.2 KiB
Bash

# Sourced by managed-container; uses the shared OCF and parameter environment.
pg_remote_query()
{
local sql="$1"
pg_replication_exec \
psql \
"host=${REJOIN_SOURCE_HOST} port=${REJOIN_SOURCE_PORT} dbname=postgres user=${REPLICATION_USER}" \
-Atqc "${sql}"
}
ensure_local_ha_slots()
{
local slots raw_slot slot slot_type
#
# POSTGRES_HA_SLOTS lives in the container environment supplied by
# postgres.env. It is the cluster-wide list of physical slots that
# every writable primary must provide.
#
slots="$(
pg_command '
printf "%s" "${POSTGRES_HA_SLOTS:-}"
' 2>/dev/null
)" || {
ocf_exit_reason \
"Unable to read POSTGRES_HA_SLOTS from PostgreSQL container"
return "${OCF_ERR_GENERIC}"
}
if [ -z "${slots}" ]; then
ocf_log warn \
"POSTGRES_HA_SLOTS is empty; no local HA slots will be reconciled"
return "${OCF_SUCCESS}"
fi
while IFS= read -r raw_slot; do
slot="$(printf '%s' "${raw_slot}" | tr -d '[:space:]')"
[ -n "${slot}" ] || continue
case "${slot}" in
*[!a-z0-9_]*)
ocf_exit_reason \
"Invalid physical replication slot name: ${slot}"
return "${OCF_ERR_CONFIGURED}"
;;
esac
slot_type="$(
pg_command "
psql \
-U '${DB_USER}' \
-d postgres \
-At \
-v ON_ERROR_STOP=1 \
-c \"
SELECT slot_type
FROM pg_replication_slots
WHERE slot_name = '${slot}';
\"
" 2>/dev/null
)" || {
ocf_exit_reason \
"Failed querying replication slot ${slot}"
return "${OCF_ERR_GENERIC}"
}
slot_type="$(printf '%s' "${slot_type}" | tr -d '[:space:]')"
case "${slot_type}" in
physical)
ocf_log info \
"Physical HA slot already exists: ${slot}"
;;
"")
ocf_log info \
"Creating physical HA slot: ${slot}"
if ! pg_command "
psql \
-U '${DB_USER}' \
-d postgres \
-v ON_ERROR_STOP=1 \
-c \"
SELECT pg_create_physical_replication_slot('${slot}');
\"
" >/dev/null
then
ocf_exit_reason \
"Failed creating physical replication slot ${slot}"
return "${OCF_ERR_GENERIC}"
fi
;;
*)
ocf_exit_reason \
"Replication slot ${slot} exists but is type ${slot_type}"
return "${OCF_ERR_CONFIGURED}"
;;
esac
done <<EOF
$(printf '%s\n' "${slots}" | tr ',' '\n')
EOF
return "${OCF_SUCCESS}"
}
ensure_remote_slot()
{
local host="${1:-}"
local slot="${2:-}"
local result
[ -n "${host}" ] || return 1
[ -n "${slot}" ] || return 1
result="$(
pg_replication_exec \
psql \
"host=${host} port=${REJOIN_SOURCE_PORT} user=${REPLICATION_USER} dbname=postgres" \
-Atc "
SELECT slot_type
FROM pg_replication_slots
WHERE slot_name = '${slot}';
" 2>/dev/null
)" || return 1
case "${result}" in
physical)
return 0
;;
"")
if ! pg_replication_exec \
psql \
"host=${host} port=${REJOIN_SOURCE_PORT} user=${REPLICATION_USER} dbname=postgres" \
-Atc "
SELECT slot_name
FROM pg_create_physical_replication_slot('${slot}');
" >/dev/null
then
return 1
fi
return 0
;;
*)
ocf_log err \
"Replication slot ${slot} exists on ${host} but is not physical"
return 1
;;
esac
}
candidate_caught_up()
{
local candidate="${1:-}"
local result
[ -n "${candidate}" ] || return 1
result="$(pg_command "
psql -U '${DB_USER}' -d '${DB_NAME}' -Atc \"
SELECT CASE
WHEN state = 'streaming'
AND replay_lsn >= pg_current_wal_flush_lsn()
THEN 'yes'
ELSE 'no'
END
FROM pg_stat_replication
WHERE application_name = '${candidate}'
LIMIT 1;
\"
" 2>/dev/null)" || return 1
[ "${result}" = "yes" ]
}
candidate_is_standby()
{
local host="${1:-}"
local result
local port="${REJOIN_SOURCE_PORT:-5432}"
[ -n "${host}" ] || return 1
result="$(
pg_replication_exec \
psql \
"host=${host} port=${port} user=${REPLICATION_USER} dbname=postgres" \
-Atc "SELECT pg_is_in_recovery();" \
2>/dev/null
)" || return 1
[ "${result}" = "t" ]
}