Quiescence Script Examples¶
Example quiescence scripts for PostgreSQL, MySQL, MariaDB, and SQLite. Read Application-Consistent Backups first for how the agent runs these (timing, permissions, timeout) before adapting them to your setup.
PostgreSQL¶
PostgreSQL brackets a backup with pg_backup_start()/pg_backup_stop() on 15+, or pg_start_backup()/pg_stop_backup() on older versions — the two work differently enough to need separate scripts.
PostgreSQL 15+¶
Warning
pg_backup_start()'s non-exclusive backup mode ends when the calling session closes. A plain psql -c "SELECT pg_backup_start(...)" call closes its connection as soon as the statement returns, ending backup mode before the snapshot is taken. This script holds one psql session open across PRE and POST via a named pipe.
#!/bin/bash
set -euo pipefail
RUN_DIR="/run/slide-quiescence-postgresql"
CMD_FIFO="$RUN_DIR/cmd.fifo"
OUT_FILE="$RUN_DIR/out.log"
case "$1" in
PRE)
mkdir -p "$RUN_DIR"
rm -f "$CMD_FIFO" "$OUT_FILE"
mkfifo "$CMD_FIFO"
# FIFO as psql's stdin avoids EOF; backgrounded, it keeps backup mode
# open until POST tells it to quit.
sudo -u postgres psql --quiet --no-psqlrc <>"$CMD_FIFO" >"$OUT_FILE" 2>&1 &
exec 8>"$CMD_FIFO"
echo "SELECT pg_backup_start(label => 'slide-quiescence', fast => true);" >&8
echo "SELECT 'slide-backup-started';" >&8
exec 8>&-
# Poll for confirmation, staying under the 30s script timeout.
for _ in $(seq 1 20); do
grep -q slide-backup-started "$OUT_FILE" && exit 0
sleep 0.5
done
echo "postgresql backup mode was not confirmed in time" >&2
exit 1
;;
POST)
exec 8>"$CMD_FIFO"
echo "SELECT pg_backup_stop();" >&8
echo "\\q" >&8
exec 8>&-
rm -rf "$RUN_DIR"
;;
esac
sudo -u postgres assumes peer auth (default on most installs) — see Authenticating without peer auth otherwise.
PostgreSQL Pre-15¶
Exclusive backup mode persists via an on-disk backup_label file rather than the session, so plain psql -c calls work fine — no FIFO needed:
case "$1" in
PRE)
sudo -u postgres psql -c "SELECT pg_start_backup('slide-quiescence', true);"
;;
POST)
sudo -u postgres psql -c "SELECT pg_stop_backup();"
;;
esac
Authenticating without peer auth¶
Use a .pgpass file instead of embedding credentials, owned by root, mode 0600, format hostname:port:database:username:password. Export these before either script above, and drop sudo -u postgres:
export PGPASSFILE="/etc/slide/agent/quiescence.d/.pgpass"
export PGHOST="localhost"
export PGUSER="postgres"
MySQL/MariaDB¶
FLUSH TABLES WITH READ LOCK requires the RELOAD privilege. Identical for MySQL and MariaDB.
Warning
The read lock releases the instant its connection closes, and mysql --batch fully buffers stdout when it isn't a TTY — so this script needs both --unbuffered and a connection held open via a named pipe across PRE and POST.
Example script¶
#!/bin/bash
set -euo pipefail
RUN_DIR="/run/slide-quiescence-mysql"
CMD_FIFO="$RUN_DIR/cmd.fifo"
OUT_FILE="$RUN_DIR/out.log"
case "$1" in
PRE)
mkdir -p "$RUN_DIR"
rm -f "$CMD_FIFO" "$OUT_FILE"
mkfifo "$CMD_FIFO"
# FIFO as the client's stdin avoids EOF; backgrounded, it keeps
# holding the lock until POST tells it to quit. --unbuffered stops
# mysql from delaying the confirmation query's output.
mysql --defaults-extra-file=/etc/slide/agent/quiescence.d/.my.cnf \
--unbuffered --batch <>"$CMD_FIFO" >"$OUT_FILE" 2>&1 &
exec 8>"$CMD_FIFO"
echo "FLUSH TABLES WITH READ LOCK;" >&8
echo "SELECT 'slide-lock-acquired';" >&8
exec 8>&-
# Poll for confirmation, staying under the 30s script timeout.
for _ in $(seq 1 20); do
grep -q slide-lock-acquired "$OUT_FILE" && exit 0
sleep 0.5
done
echo "mysql read lock was not acquired in time" >&2
exit 1
;;
POST)
exec 8>"$CMD_FIFO"
echo "UNLOCK TABLES;" >&8
echo "\\q" >&8
exec 8>&-
rm -rf "$RUN_DIR"
;;
esac
Authenticating¶
.my.cnf must be owned by root, mode 0600.
SQLite¶
In WAL mode, a snapshot taken mid-write can catch the main file and -wal file out of sync. Checkpointing first folds the WAL back into the main file.
Example script¶
#!/bin/bash
set -euo pipefail
DATABASES=(
"/var/lib/myapp/data.db"
)
case "$1" in
PRE)
for db in "${DATABASES[@]}"; do
sqlite3 "$db" "PRAGMA busy_timeout=5000; PRAGMA wal_checkpoint(TRUNCATE);"
done
;;
POST)
# No-op — nothing to resume.
;;
esac
List every database file your application uses in DATABASES. wal_checkpoint(TRUNCATE) is a no-op if the database isn't in WAL mode, so this works either way.