#!/bin/bash
# Deterministic reproducer: autovacuum launcher TRAP in pgstat_tracks_io_op()
# (pgstat_io.c:74) caused by an IOOP_EXTEND of the visibility map.
#
# Mechanism (commits 4f7ecca84dd + 378a216187a on HEAD):
#   - The autovacuum launcher's get_database_list() seqscans pg_database without
#     SO_HINT_REL_READ_ONLY. If a page is FULL and its pd_prune_xid is removable
#     and the _vm fork is missing, pruning extends the VM -> IOOP_EXTEND, which
#     pgstat_tracks_io_op() asserts the launcher never does -> TRAP.
#
# The trigger is a race: the launcher must be the FIRST thing to scan a full,
# unpruned pg_database page while _vm is absent. Any other backend that scans
# first prunes the page (clearing pd_prune_xid) and recreates _vm harmlessly,
# closing the window. We make it deterministic by:
#   1. autovacuum OFF during the fill, plus a held REPEATABLE READ snapshot that
#      pins the xmin horizon so NOTHING is removable -> nothing prunes, so the
#      full pages keep their pd_prune_xid.
#   2. stop (drops the holder), delete the _vm fork to simulate vm extend.
#   3. restart with autovacuum ON + huge thresholds (so no worker is scheduled):
#      the launcher's get_database_list scan is the first/only scanner of
#      pg_database and trips the assert.
#
# Override the install/data locations if your layout differs:
#   PGBIN=/path/to/pg/bin PGDATA=/path/to/datadir ./av_launcher_vm_extend_repro.sh
set -u
BIN="${PGBIN:-$HOME/postgres-install/bin}"
PGDATA="${PGDATA:-/tmp/avrepro-pgdata}"
PORT="${PGPORT:-5440}"
LOG=/tmp/avrepro-pg.log
FP="global/1262"   # pg_database (mapped) main fork

crashed() { grep -qE "pgstat_tracks_io_op|autovacuum launcher process.*was terminated by signal" "$LOG" 2>/dev/null; }

$BIN/pg_ctl -D "$PGDATA" -m immediate stop >/dev/null 2>&1
rm -rf "$PGDATA" "$LOG"
$BIN/initdb -D "$PGDATA" -U postgres --auth=trust >/tmp/avrepro-initdb.log 2>&1 || { echo initdb failed; exit 1; }
cat >> "$PGDATA/postgresql.conf" <<EOF
fsync = off
port = $PORT
listen_addresses = 'localhost'
autovacuum = on
autovacuum_naptime = 1s
autovacuum_vacuum_threshold = 2000000000
autovacuum_vacuum_insert_threshold = 2000000000
autovacuum_freeze_max_age = 2000000000
EOF

# --- Phase 1: fill pg_database with autovacuum OFF and a held snapshot ---------
$BIN/pg_ctl -D "$PGDATA" -l "$LOG" -w -o "-c autovacuum=off" start >/dev/null 2>&1 \
  || { echo start failed; tail "$LOG"; exit 1; }

# Hold a REPEATABLE READ snapshot open to pin the xmin horizon for the whole fill
# so no insert's pd_prune_xid is ever "removable" -> nothing prunes pg_database.
$BIN/psql -U postgres -p $PORT -d postgres >/tmp/avrepro-holder.log 2>&1 <<'SQL' &
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT pg_current_xact_id();
SELECT pg_sleep(600);
COMMIT;
SQL
HOLDER_PID=$!
sleep 1   # let the holder establish its snapshot before we start inserting

echo "[repro] filling pg_database with 220 databases (autovacuum off, snapshot held)..."
for i in $(seq 1 220); do echo "CREATE DATABASE reprodb_$i;"; done | $BIN/psql -U postgres -p $PORT -d postgres -q >/dev/null 2>&1
echo "[repro] pg_database size: $($BIN/psql -U postgres -p $PORT -d postgres -tAq -c "SELECT pg_relation_size('pg_database')") bytes"

kill "$HOLDER_PID" 2>/dev/null

# --- Phase 2: open the window, restart with autovacuum ON ----------------------
$BIN/pg_ctl -D "$PGDATA" -m fast stop >/dev/null 2>&1
rm -f "$PGDATA/${FP}_vm"            # delete VM fork -> next prune must extend it
: > "$LOG"
$BIN/pg_ctl -D "$PGDATA" -l "$LOG" -w start >/dev/null 2>&1

# The launcher scans pg_database within ~naptime; do NOT connect any client.
for s in $(seq 1 20); do
  crashed && break
  sleep 1
done
echo "[repro] launcher alive=$($BIN/pg_ctl -D "$PGDATA" status >/dev/null 2>&1 && echo yes || echo NO)  vm_fork=$(test -f "$PGDATA/${FP}_vm" && echo recreated || echo absent)"

echo "===================== RESULT ====================="
if crashed; then
  echo ">>> REPRODUCED: launcher assert/crash"
  grep -nE "TRAP:|pgstat_tracks_io_op|autovacuum launcher process.*terminated|signal 6" "$LOG" | head
else
  echo ">>> NO CRASH"
fi
echo "=================================================="
$BIN/pg_ctl -D "$PGDATA" -m immediate stop >/dev/null 2>&1
