011: observability - pg_stat_statements, slow-log, io timing

preload pg_stat_statements (+extension in iwanhae/postgres DBs),
log_min_duration_statement=500ms, track_io_timing=on.
This commit is contained in:
wan
2026-08-31 22:21:53 +09:00
parent e7cfa261c8
commit ba1b6a4393
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# 011-postgres-observability - Turn on the instrumentation that any future
# tuning depends on (A-tier of the 2026-08 tuning review).
#
# shared_preload_libraries = 'pg_stat_statements' query stats (restart)
# track_io_timing = on pg_stat_io timing, cheap on modern Linux
# log_min_duration_statement = 500ms slow queries land in the server log
#
# pg_stat_statements extension is created in `iwanhae` (app db) and
# `postgres` (admin db). Inspect with:
# SELECT calls, round(total_exec_time::numeric,1) ms, rows, query
# FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
# up: write drop-in; restart only if the lib is not yet loaded; extensions
# down: drop extensions (loses collected stats), remove drop-in, restart
set -euo pipefail
PGBIN=/etc/postgresql/18/main
CONF=$PGBIN/conf.d/011-postgres-observability.conf
CLUSTER=$(pg_lsclusters --no-header | awk 'NR==1{print $1"/"$2}')
DBS=(iwanhae postgres)
pg() { runuser -u postgres -- psql -d postgres -v ON_ERROR_STOP=1 -tAc "$1"; }
lib_loaded() { pg 'SHOW shared_preload_libraries' | grep -q pg_stat_statements; }
write_conf() {
install -d -m 755 "$PGBIN/conf.d"
cat > "$CONF" <<'EOF'
# managed by sysmig 011-postgres-observability
shared_preload_libraries = 'pg_stat_statements'
track_io_timing = on
log_min_duration_statement = 500ms
EOF
chown root:postgres "$CONF"
chmod 644 "$CONF"
}
case "${1:-}" in
up)
write_conf
if lib_loaded; then
pg_ctlcluster "$CLUSTER" reload
echo " cluster reloaded (pg_stat_statements already loaded)"
else
pg_ctlcluster "$CLUSTER" restart
pg_isready -q
echo " cluster restarted to load pg_stat_statements"
fi
for db in "${DBS[@]}"; do
runuser -u postgres -- psql -d "$db" -v ON_ERROR_STOP=1 -qc \
'CREATE EXTENSION IF NOT EXISTS pg_stat_statements'
done
lib_loaded || { echo "FATAL: pg_stat_statements not loaded" >&2; exit 1; }
[[ $(pg 'SHOW track_io_timing') == on ]] || { echo "FATAL: track_io_timing not on" >&2; exit 1; }
[[ $(pg 'SHOW log_min_duration_statement') == '500ms' ]] || { echo "FATAL: log_min_duration_statement wrong" >&2; exit 1; }
echo " extension created in: ${DBS[*]}"
;;
status)
if [[ $EUID -eq 0 ]]; then
q() { runuser -u postgres -- psql -d "$1" -v ON_ERROR_STOP=1 -tAc "$2"; }
elif psql -d postgres -tAc 'SELECT 1' >/dev/null 2>&1; then
q() { psql -d "$1" -v ON_ERROR_STOP=1 -tAc "$2"; }
else
echo "? needs root or peer DB access to verify"
exit 2
fi
[[ -f $CONF ]] || { echo "DRIFT drop-in $CONF missing"; exit 1; }
if ! q postgres 'SHOW shared_preload_libraries' | grep -q pg_stat_statements; then
echo "DRIFT pg_stat_statements not preloaded"; exit 1
fi
[[ $(q postgres 'SHOW track_io_timing') == on ]] || { echo "DRIFT track_io_timing off"; exit 1; }
[[ $(q postgres 'SHOW log_min_duration_statement') == '500ms' ]] || { echo "DRIFT log_min_duration_statement"; exit 1; }
for db in "${DBS[@]}"; do
[[ $(q "$db" "SELECT count(*) FROM pg_extension WHERE extname='pg_stat_statements'") -ge 1 ]] \
|| { echo "DRIFT extension missing in $db"; exit 1; }
done
echo "OK pg_stat_statements + slow-log(500ms) + io timing"
;;
down)
for db in "${DBS[@]}"; do
runuser -u postgres -- psql -d "$db" -v ON_ERROR_STOP=1 -qc \
'DROP EXTENSION IF EXISTS pg_stat_statements' \
|| { echo "cannot drop extension in $db (in use?) - retry later" >&2; exit 1; }
done
rm -f "$CONF"
if lib_loaded; then
pg_ctlcluster "$CLUSTER" restart
echo " extensions dropped, drop-in removed, cluster restarted to unload lib"
else
echo " extensions dropped, drop-in removed (lib was not loaded)"
fi
;;
esac