From efb0e79d752102dd0fa066959cea517288cc6085 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Wed, 26 Jan 2022 19:24:21 +0100
Subject: [PATCH 1/2] Add TAP test

---
 src/test/recovery/t/028_wraparound_restart.pl | 172 ++++++++++++++++++
 1 file changed, 172 insertions(+)
 create mode 100644 src/test/recovery/t/028_wraparound_restart.pl

diff --git a/src/test/recovery/t/028_wraparound_restart.pl b/src/test/recovery/t/028_wraparound_restart.pl
new file mode 100644
index 00000000000..2be5537b6b3
--- /dev/null
+++ b/src/test/recovery/t/028_wraparound_restart.pl
@@ -0,0 +1,172 @@
+# Run the standard regression tests with streaming replication
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 36;
+use File::Basename;
+
+# Initialize primary node
+my $node_primary = PostgreSQL::Test::Cluster->new('primary');
+$node_primary->init(allows_streaming => 1);
+$node_primary->adjust_conf('postgresql.conf', 'max_connections', '25', 1);
+$node_primary->append_conf('postgresql.conf', 'max_prepared_transactions = 10');
+
+# needed so that vacuumdb freezes the template too, and we don't get warnings
+# about possible data loss due to wraparound
+$node_primary->start;
+$node_primary->psql(
+        'postgres',
+        qq[UPDATE pg_database SET datallowconn = 't']);
+$node_primary->stop;
+
+# reset WAL close to max XID (4294967295), in 4 smaller steps (~1B each)
+command_checks_all(
+	[ 'pg_resetwal', '-x', 1000000000, $node_primary->data_dir ],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 1B XID');
+
+command_checks_all(
+	[ 'dd', 'if=/dev/zero', 'of=' . $node_primary->data_dir . '/pg_xact/03B9', 'bs=1K', 'count=256'],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 1B XID');
+
+$node_primary->start;
+$node_primary->issues_sql_like(
+	[ 'vacuumdb', '--all', '--freeze', '--echo' ],
+	qr/statement: VACUUM.*statement: VACUUM/s,
+	'vacuum all databases');
+$node_primary->stop;
+
+command_checks_all(
+	[ 'pg_resetwal', '-x', 2000000000, $node_primary->data_dir ],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 2B XID');
+
+command_checks_all(
+	[ 'dd', 'if=/dev/zero', 'of=' . $node_primary->data_dir . '/pg_xact/0773', 'bs=1K', 'count=256'],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 2B XID');
+
+$node_primary->start;
+$node_primary->issues_sql_like(
+	[ 'vacuumdb', '--all', '--freeze', '--echo' ],
+	qr/statement: VACUUM.*statement: VACUUM/s,
+	'vacuum all databases');
+$node_primary->stop;
+
+command_checks_all(
+	[ 'pg_resetwal', '-x', 3000000000, $node_primary->data_dir ],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 3B XID');
+
+command_checks_all(
+	[ 'dd', 'if=/dev/zero', 'of=' . $node_primary->data_dir . '/pg_xact/0B2D', 'bs=1K', 'count=256'],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 3B XID');
+
+$node_primary->start;
+
+is( $node_primary->psql(
+        'postgres',
+        qq[SELECT datname
+    , age(datfrozenxid)
+    , current_setting('autovacuum_freeze_max_age') 
+FROM pg_database 
+ORDER BY 2 DESC]),
+    0,
+    'physical slot created on primary');
+
+$node_primary->issues_sql_like(
+	[ 'vacuumdb', '--all', '--freeze', '--echo' ],
+	qr/statement: VACUUM.*statement: VACUUM/s,
+	'vacuum all databases');
+$node_primary->stop;
+
+# the max is 4294967295, so this is within 100 transactions of wraparound
+command_checks_all(
+	[ 'pg_resetwal', '-x', 4294967200, $node_primary->data_dir ],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 4B XID');
+
+command_checks_all(
+	[ 'dd', 'if=/dev/zero', 'of=' . $node_primary->data_dir . '/pg_xact/0FFF', 'bs=1K', 'count=256'],
+	0,
+	[qr''],
+	[qr''],
+	'reset WAL to 4B XID');
+
+$node_primary->start;
+$node_primary->issues_sql_like(
+	[ 'vacuumdb', '--all', '--freeze', '--echo' ],
+	qr/statement: VACUUM.*statement: VACUUM/s,
+	'vacuum all databases');
+
+
+# now, start transactions with XID before/after the 4B limit
+my $main_in    = '';
+my $main_out   = '';
+my $main_timer = IPC::Run::timeout(180);
+
+my $main_h =
+  $node_primary->background_psql('postgres', \$main_in, \$main_out,
+	$main_timer, on_error_stop => 1);
+$main_in .= q(
+BEGIN;
+SELECT txid_current();
+\echo syncpoint1
+);
+pump $main_h until $main_out =~ /syncpoint1/ || $main_timer->is_expired;
+
+# run 200 trivial transactions to consume enough XIDs and cross 4B
+$node_primary->pgbench(
+	"-t 200 -n",
+	0,
+	[qr{}],
+	[qr{^$}],
+	'pgbench custom scripts',
+	{
+		'001_pgbench_custom_script_1@1' => q{-- select only
+SELECT txid_current();
+}
+	});
+
+$main_timer = IPC::Run::timeout(180);
+$main_h =
+  $node_primary->background_psql('postgres', \$main_in, \$main_out,
+	$main_timer, on_error_stop => 1);
+$main_in .= q(
+BEGIN;
+SELECT txid_current();
+\echo syncpoint1
+);
+pump $main_h until $main_out =~ /syncpoint1/ || $main_timer->is_expired;
+
+# Take backup and try starting a replica from the backup
+my $backup_name = 'my_backup';
+
+$node_primary->backup($backup_name);
+
+# Create streaming standby linking to primary
+my $node_standby_1 = PostgreSQL::Test::Cluster->new('standby_1');
+$node_standby_1->init_from_backup($node_primary, $backup_name,
+	has_streaming => 1);
+    
+$node_standby_1->start;
+
+$node_standby_1->stop;
+$node_primary->stop;
-- 
2.31.1

