max_connections 100->30, effective_cache_size 768MB, statement_timeout 30s, idle_in_transaction_session_timeout 60s, temp_file_limit 2GB via conf.d drop-in; restart only when max_connections changes.
101 lines
3.7 KiB
Bash
101 lines
3.7 KiB
Bash
#!/usr/bin/env bash
|
|
# 010-tune-postgres - Memory & stability tuning for the 1GB e2-micro host
|
|
# (B-tier of the 2026-08 tuning review). Settings land as a conf.d drop-in;
|
|
# postgresql.conf itself stays pristine (include_dir = 'conf.d' is stock).
|
|
#
|
|
# max_connections 100 -> 30 each backend costs MBs; real
|
|
# concurrency comes from PgBouncer
|
|
# (013). 30 = pool 20 + admin headroom
|
|
# effective_cache_size 4GB -> 768MB planner hint; box has 1GB, the 4GB
|
|
# default overrates cache -> bad plans
|
|
# work_mem 4MB (explicit, equals default; kept for determinism)
|
|
# statement_timeout 0 -> 30s runaway-query guard; long migrations:
|
|
# SET statement_timeout = 0; in-session
|
|
# idle_in_transaction_session_timeout 0 -> 60s leaked transactions block
|
|
# vacuum and hold locks; kill them
|
|
# temp_file_limit -1 -> 2GB per-backend sort-spill cap so one bad
|
|
# query cannot fill the 20G data disk
|
|
# up: write drop-in; restart only if max_connections changes, else reload
|
|
# down: remove drop-in; restart (restoring max_connections needs it)
|
|
set -euo pipefail
|
|
|
|
PGBIN=/etc/postgresql/18/main
|
|
CONF=$PGBIN/conf.d/010-tune-postgres.conf
|
|
CLUSTER=$(pg_lsclusters --no-header | awk 'NR==1{print $1"/"$2}')
|
|
|
|
# desired state as "guc value" pairs; SHOW renders exactly these strings
|
|
WANT=(
|
|
'max_connections 30'
|
|
'effective_cache_size 768MB'
|
|
'work_mem 4MB'
|
|
'statement_timeout 30s'
|
|
'idle_in_transaction_session_timeout 60s'
|
|
'temp_file_limit 2GB'
|
|
)
|
|
|
|
pg() { runuser -u postgres -- psql -d postgres -v ON_ERROR_STOP=1 -tAc "$1"; }
|
|
|
|
write_conf() {
|
|
install -d -m 755 "$PGBIN/conf.d"
|
|
cat > "$CONF" <<'EOF'
|
|
# managed by sysmig 010-tune-postgres - memory & stability for 1GB e2-micro
|
|
max_connections = 30
|
|
effective_cache_size = 768MB
|
|
work_mem = 4MB
|
|
statement_timeout = 30s
|
|
idle_in_transaction_session_timeout = 60s
|
|
temp_file_limit = 2GB
|
|
EOF
|
|
chown root:postgres "$CONF"
|
|
chmod 644 "$CONF"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
up)
|
|
write_conf
|
|
if [[ $(pg 'SHOW max_connections') != 30 ]]; then
|
|
pg_ctlcluster "$CLUSTER" restart
|
|
pg_isready -q
|
|
echo " cluster restarted (max_connections requires it)"
|
|
else
|
|
pg_ctlcluster "$CLUSTER" reload
|
|
echo " cluster reloaded"
|
|
fi
|
|
for kv in "${WANT[@]}"; do
|
|
read -r guc want <<<"$kv"
|
|
got=$(pg "SHOW $guc")
|
|
[[ $got == "$want" ]] || { echo "FATAL: $guc is '$got', expected '$want'" >&2; exit 1; }
|
|
done
|
|
echo " ${#WANT[@]} memory/stability settings active"
|
|
;;
|
|
status)
|
|
if [[ $EUID -eq 0 ]]; then
|
|
q() { runuser -u postgres -- psql -d postgres -v ON_ERROR_STOP=1 -tAc "$1"; }
|
|
elif psql -d postgres -tAc 'SELECT 1' >/dev/null 2>&1; then
|
|
q() { psql -d postgres -v ON_ERROR_STOP=1 -tAc "$1"; }
|
|
else
|
|
echo "? needs root or peer DB access to verify"
|
|
exit 2
|
|
fi
|
|
[[ -f $CONF ]] || { echo "DRIFT drop-in $CONF missing"; exit 1; }
|
|
bad=()
|
|
for kv in "${WANT[@]}"; do
|
|
read -r guc want <<<"$kv"
|
|
got=$(q "SHOW $guc" 2>/dev/null)
|
|
[[ $got == "$want" ]] || bad+=("$guc=$got(want $want)")
|
|
done
|
|
if ((${#bad[@]})); then echo "DRIFT ${bad[*]}"; exit 1; fi
|
|
echo "OK ${#WANT[@]} settings active (max_connections=30, timeouts, temp_file_limit)"
|
|
;;
|
|
down)
|
|
rm -f "$CONF"
|
|
if [[ $(pg 'SHOW max_connections') == 30 ]]; then
|
|
pg_ctlcluster "$CLUSTER" restart
|
|
echo " drop-in removed, cluster restarted (defaults restored)"
|
|
else
|
|
pg_ctlcluster "$CLUSTER" reload
|
|
echo " drop-in removed, cluster reloaded"
|
|
fi
|
|
;;
|
|
esac
|