|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# CONFIGURATION |
| 4 | +INSTALL_DIR="$HOME/postgresql/bld_18.1.1/install" |
| 5 | +PGDATA="$INSTALL_DIR/data" |
| 6 | +LOGFILE=$PGDATA/server.log |
| 7 | +ARCHIVE_DIR="$INSTALL_DIR/wal_archive" |
| 8 | +BACKUP_DIR="$INSTALL_DIR/base_backup" |
| 9 | +PORT=5432 |
| 10 | + |
| 11 | +# Cleanup from previous runs |
| 12 | +PID=$(lsof -ti :$PORT) |
| 13 | +if [ -n "$PID" ]; then |
| 14 | + kill -9 $PID |
| 15 | +fi |
| 16 | +rm -rf "$PGDATA" "$ARCHIVE_DIR" "$BACKUP_DIR" "${PGDATA}_bk" "/tmp/keyring.per" |
| 17 | +rm -rf /dev/shm/* |
| 18 | +mkdir -p "$ARCHIVE_DIR" |
| 19 | + |
| 20 | +echo "=== Step 1: Initialize primary server ===" |
| 21 | +"$INSTALL_DIR/bin/initdb" -D "$PGDATA" |
| 22 | + |
| 23 | +# Configure archive settings |
| 24 | +echo "shared_preload_libraries = 'pg_tde'" >> "$PGDATA/postgresql.conf" |
| 25 | +echo "io_method = 'sync'" >> "$PGDATA/postgresql.conf" |
| 26 | +echo "archive_mode = on" >> "$PGDATA/postgresql.conf" |
| 27 | +echo "archive_command = '$INSTALL_DIR/bin/pg_tde_archive_decrypt %f %p \"cp %%p $ARCHIVE_DIR/%%f\"'" >> "$PGDATA/postgresql.conf" |
| 28 | +echo "wal_level = replica" >> "$PGDATA/postgresql.conf" |
| 29 | +echo "wal_compression = on" >> "$PGDATA/postgresql.conf" |
| 30 | +echo "listen_addresses = '*'" >> "$PGDATA/postgresql.conf" |
| 31 | +echo "port = $PORT" >> "$PGDATA/postgresql.conf" |
| 32 | +echo "logging_collector = on" >> "$PGDATA/postgresql.conf" |
| 33 | +echo "log_directory = '$PGDATA'" >> "$PGDATA/postgresql.conf" |
| 34 | +echo "log_filename = 'server.log'" >> "$PGDATA/postgresql.conf" |
| 35 | +echo "log_statement = 'all'" >> "$PGDATA/postgresql.conf" |
| 36 | + |
| 37 | +echo "=== Step 2: Start PostgreSQL server ===" |
| 38 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w start |
| 39 | +sleep 2 |
| 40 | + |
| 41 | +echo "=== Step 3: Create extension, keys, and table ===" |
| 42 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "CREATE EXTENSION pg_tde;" |
| 43 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_tde_add_global_key_provider_file('global_provider', '/tmp/keyring.per');" |
| 44 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_tde_create_key_using_global_key_provider('wal_key', 'global_provider');" |
| 45 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_tde_create_key_using_global_key_provider('database_key', 'global_provider');" |
| 46 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_tde_set_server_key_using_global_key_provider('wal_key', 'global_provider');" |
| 47 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_tde_set_key_using_global_key_provider('database_key', 'global_provider');" |
| 48 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "ALTER SYSTEM SET pg_tde.wal_encrypt=ON;" |
| 49 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "CREATE TABLE mohit(id SERIAL PRIMARY KEY, name TEXT) USING tde_heap;" |
| 50 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "INSERT INTO mohit(name) VALUES('before backup 1'), ('before backup 2');" |
| 51 | + |
| 52 | +echo "=== Step 4: Restart server to enable WAL encryption ===" |
| 53 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w restart |
| 54 | +sleep 2 |
| 55 | + |
| 56 | +echo "=== Step 5: Take a base backup ===" |
| 57 | +mkdir $BACKUP_DIR |
| 58 | +chmod 700 $BACKUP_DIR |
| 59 | +cp -R $PGDATA/pg_tde $BACKUP_DIR/ |
| 60 | +"$INSTALL_DIR/bin/pg_tde_basebackup" -D "$BACKUP_DIR" -F plain -X stream -E -p $PORT |
| 61 | + |
| 62 | +echo "=== Step 5.1: Insert and capture 5 recovery target times ===" |
| 63 | +declare -A RECOVERY_TARGET_TIMES |
| 64 | +for i in {1..5}; do |
| 65 | + "$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "INSERT INTO mohit(name) VALUES('after backup $i');" |
| 66 | + sleep 2 |
| 67 | + RECOVERY_TARGET_TIMES[$i]=$("$INSTALL_DIR/bin/psql" -d postgres -p $PORT -Atc "SELECT now();") |
| 68 | + echo "Captured recovery target time $i: ${RECOVERY_TARGET_TIMES[$i]}" |
| 69 | +done |
| 70 | + |
| 71 | +# Switch WAL & force archive |
| 72 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_switch_wal();" |
| 73 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w restart |
| 74 | + |
| 75 | +############################################################ |
| 76 | +### === TEST A: No temp files after successful archive === |
| 77 | +############################################################ |
| 78 | +echo "=== TEST A: Checking that no temp files remain after success ===" |
| 79 | +if ls /dev/shm | grep -q pg_tde; then |
| 80 | + echo "ERROR: Temp files leaked on success" |
| 81 | + exit 1 |
| 82 | +else |
| 83 | + echo "OK: No temp files on success" |
| 84 | +fi |
| 85 | + |
| 86 | +############################################################### |
| 87 | +### === TEST B: Cleanup on failure of wrapped archive cmd === |
| 88 | +############################################################### |
| 89 | +echo "=== TEST B: Cleanup on failure of archive command ===" |
| 90 | + |
| 91 | +sed -i "s|cp %%p $ARCHIVE_DIR/%%f|false|" $PGDATA/postgresql.conf |
| 92 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w restart |
| 93 | + |
| 94 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT pg_switch_wal();" |
| 95 | +sleep 2 |
| 96 | + |
| 97 | +if ls /dev/shm | grep -q pg_tde; then |
| 98 | + echo "ERROR: Temp files leaked on wrapped-command failure" |
| 99 | + exit 1 |
| 100 | +else |
| 101 | + echo "OK: Cleanup successful on failure" |
| 102 | +fi |
| 103 | + |
| 104 | +# Restore original archive_command |
| 105 | +sed -i "s|false|cp %%p $ARCHIVE_DIR/%%f|" $PGDATA/postgresql.conf |
| 106 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w restart |
| 107 | + |
| 108 | +############################################################### |
| 109 | +### === TEST C: ENOSPC behavior (/dev/shm full) === |
| 110 | +############################################################### |
| 111 | +echo "=== TEST C: Insufficient tmpfs capacity (ENOSPC test) ===" |
| 112 | +rm -rf /tmp/fake_wal |
| 113 | +touch /tmp/fake_wal |
| 114 | +dd if=/dev/urandom of=/tmp/fake_wal bs=1M count=1 |
| 115 | + |
| 116 | +# Fill /dev/shm until ENOSPC |
| 117 | +echo "Filling /dev/shm..." |
| 118 | +counter=0 |
| 119 | +while :; do |
| 120 | + dd if=/dev/urandom of=/dev/shm/fill_$counter bs=1M count=50 2>/dev/null || break |
| 121 | + counter=$((counter+1)) |
| 122 | +done |
| 123 | +echo "/dev/shm filled, running pg_tde_archive_decrypt..." |
| 124 | + |
| 125 | +set +e |
| 126 | +$INSTALL_DIR/bin/pg_tde_archive_decrypt /tmp/fake_wal /tmp/fake_out "cp %f %p" |
| 127 | +RET=$? |
| 128 | +set -e |
| 129 | + |
| 130 | +if [ $RET -eq 0 ]; then |
| 131 | + echo "pg_tde_archive_decrypt command is successful" |
| 132 | +else |
| 133 | + echo "ERROR: Not successful" |
| 134 | + exit 1 |
| 135 | +fi |
| 136 | + |
| 137 | +# Cleaning up tmpfs |
| 138 | +rm -f /dev/shm/fill_* |
| 139 | + |
| 140 | +############################################################### |
| 141 | +### === TEST D: Multiple retries do NOT leak === |
| 142 | +############################################################### |
| 143 | +echo "=== TEST D: Multiple retry stress test ===" |
| 144 | + |
| 145 | +for i in {1..8}; do |
| 146 | + set +e |
| 147 | + $INSTALL_DIR/bin/pg_tde_archive_decrypt /tmp/fake_wal /tmp/fake_out "false" |
| 148 | + set -e |
| 149 | + |
| 150 | + if ls /dev/shm | grep -q pg_tde; then |
| 151 | + echo "ERROR: Leak on retry $i!" |
| 152 | + exit 1 |
| 153 | + fi |
| 154 | +done |
| 155 | + |
| 156 | +echo "OK: No leaks after repeated failures" |
| 157 | + |
| 158 | +echo "=== Printing WAL content from $ARCHIVE_DIR using strings utility ===" |
| 159 | +strings $ARCHIVE_DIR/000000010000000000000001 | grep 'before backup' |
| 160 | + |
| 161 | +echo "=== Perform recovery ===" |
| 162 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w stop |
| 163 | + |
| 164 | +rm -rf "$PGDATA" |
| 165 | +cp -r "$BACKUP_DIR" "$PGDATA" |
| 166 | + |
| 167 | +sed -i '/restore_command/d' "$PGDATA/postgresql.conf" |
| 168 | +sed -i '/recovery_target_time/d' "$PGDATA/postgresql.conf" |
| 169 | + |
| 170 | +echo "restore_command = '$INSTALL_DIR/bin/pg_tde_restore_encrypt %f %p \"cp $ARCHIVE_DIR/%%f %%p\"'" >> "$PGDATA/postgresql.conf" |
| 171 | +echo "recovery_target_time = '${RECOVERY_TARGET_TIMES[4]}'" >> "$PGDATA/postgresql.conf" |
| 172 | +touch "$PGDATA/recovery.signal" |
| 173 | + |
| 174 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" -o "-p $PORT" -w start |
| 175 | +sleep 5 |
| 176 | + |
| 177 | +echo "=== Promote the server ===" |
| 178 | +"$INSTALL_DIR/bin/pg_ctl" -D "$PGDATA" promote |
| 179 | +sleep 3 |
| 180 | + |
| 181 | +echo "=== Verify post-promotion data ===" |
| 182 | +"$INSTALL_DIR/bin/psql" -d postgres -p $PORT -c "SELECT * FROM mohit;" |
| 183 | + |
0 commit comments