repro-btree-reuse.sh
application/x-sh
Filename: repro-btree-reuse.sh
Type: application/x-sh
Part: 0
#! /bin/sh
set -ex
PGDATA=repro-btree-reuse
PGDATABASE=repro_btree_reuse
export PGDATA PGDATABASE PGPORT
unset PGUSER
# master
PGPORT=1
initdb
cat >$PGDATA/pg_hba.conf <<EOF
local all all ident
local replication all ident
EOF
cat >$PGDATA/postgresql.conf <<EOF
listen_addresses = ''
logging_collector = on
autovacuum = off
hot_standby = on
wal_level = hot_standby
archive_mode = on
archive_command = ':'
max_wal_senders = 1
wal_keep_segments = 5
vacuum_defer_cleanup_age = 100
max_standby_archive_delay = 0
max_standby_streaming_delay = 0
EOF
pg_ctl -w start
createdb
psql -c "SELECT pg_start_backup('foo', true)"
cp -a $PGDATA standby_$PGDATA
psql -c 'SELECT pg_stop_backup()'
# standby
PGPORT=2
rm standby_$PGDATA/postmaster.pid
cat >standby_$PGDATA/recovery.conf <<EOF
standby_mode = 'on'
primary_conninfo = 'port=1'
EOF
pg_ctl -w -D standby_$PGDATA start
# Stage some B-tree pages full of pointers to dead tuples.
PGPORT=1 psql -X <<'EOSQL'
CREATE TABLE t (x) AS SELECT n FROM generate_series(1,1000) t(n);
CREATE INDEX ON t(x);
DELETE FROM t;
-- Burn vacuum_defer_cleanup_age XIDs, to make the tuples HEAPTUPLE_DEAD.
CREATE OR REPLACE FUNCTION pg_temp.consume_xids(int) RETURNS void
LANGUAGE plpgsql AS $$
BEGIN
FOR i IN 1 .. $1 LOOP
BEGIN
SELECT 1 FROM pg_class WHERE oid = 't'::regclass FOR SHARE LIMIT 1;
RAISE EXCEPTION 'croak';
EXCEPTION WHEN OTHERS THEN END;
END LOOP;
END
$$;
SELECT pg_temp.consume_xids(110);
EOSQL
sleep 1
# Any long-running command; its snapshot that will conflict with recovery.
PGPORT=2 psql -c 'SELECT pg_sleep(600)' &
# Mark the pages BTP_DELETED, then reuse them.
PGPORT=1 psql -X <<EOSQL
VACUUM t;
VACUUM t; -- second VACUUM required to make the reuse happen
INSERT INTO t SELECT n FROM generate_series(1,1000) t(n);
EOSQL
sleep 1
PGPORT=2 pg_ctl -w -D standby_$PGDATA -m fast stop
PGPORT=1 pg_ctl -w -m fast stop