From 557b22e931233e336704d04defee2e19c7706d1c Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 7 Nov 2025 17:21:26 +0200 Subject: [PATCH 1/2] Add test for vacuuming at multixid wraparound This currently fails. The next commit fixes the failure. This isn't fully polished, and I'm not sure if it's worth committing. --- src/test/modules/test_misc/meson.build | 1 + .../test_misc/t/010_mxid_wraparound.pl | 123 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/test/modules/test_misc/t/010_mxid_wraparound.pl diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build index f258bf1ccd9..cf57ed21dc6 100644 --- a/src/test/modules/test_misc/meson.build +++ b/src/test/modules/test_misc/meson.build @@ -18,6 +18,7 @@ tests += { 't/007_catcache_inval.pl', 't/008_replslot_single_user.pl', 't/009_log_temp_files.pl', + 't/010_mxid_wraparound.pl', ], }, } diff --git a/src/test/modules/test_misc/t/010_mxid_wraparound.pl b/src/test/modules/test_misc/t/010_mxid_wraparound.pl new file mode 100644 index 00000000000..487cb71eacc --- /dev/null +++ b/src/test/modules/test_misc/t/010_mxid_wraparound.pl @@ -0,0 +1,123 @@ +# +# Copyright (c) 2025, PostgreSQL Global Development Group +# +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +sub print_controldata_info +{ + my $node = shift; + my ($stdout, $stderr) = run_command([ 'pg_controldata', $node->data_dir ]); + + foreach (split("\n", $stdout)) + { + if ($_ =~ /^Latest checkpoint's Next\s*(.*)$/mg or + $_ =~ /^Latest checkpoint's oldest\s*(.*)$/mg) + { + print $_."\n"; + } + } +} + +sub create_mxid +{ + my $node = shift; + my $conn1 = $node->background_psql('postgres'); + my $conn2 = $node->background_psql('postgres'); + + $conn1->query_safe(qq( + BEGIN; + SELECT * FROM test_table WHERE id = 1 FOR SHARE; + )); + $conn2->query_safe(qq( + BEGIN; + SELECT * FROM test_table WHERE id = 1 FOR SHARE; + )); + + $conn1->query_safe(qq(COMMIT;)); + $conn2->query_safe(qq(COMMIT;)); + + $conn1->quit; + $conn2->quit; +} + +# 1) Create test cluster +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init; + +$node->start; + +$node->safe_psql('postgres', +qq( + CREATE TABLE test_table (id integer NOT NULL PRIMARY KEY, val text); + INSERT INTO test_table VALUES (1, 'a'); +)); + +create_mxid($node); + +$node->safe_psql('postgres', qq(UPDATE pg_database SET datallowconn = TRUE WHERE datname = 'template0';)); +$node->stop; + +# 2) Advance mxid to UINT32_MAX. We do it in three steps, with vacuums in between, to avoid +# causing a situation where datminmxid has already wrapped around + +# Step 1 +command_ok( + [ 'pg_resetwal', '-m', '1492123648,1', $node->data_dir ], + 'approaching the mxid limit'); +$node->start; +create_mxid($node); +$node->command_ok([ 'vacuumdb', '-a', '--freeze' ], 'vacuum all databases'); +$node->stop; + +print ">>> pg_controldata: \n"; +print_controldata_info($node); + +# Step 2 +command_ok( + [ 'pg_resetwal', '-m', '2984247296,1492123648', $node->data_dir ], + 'approaching the mxid limit'); +$node->start; +create_mxid($node); +$node->command_ok([ 'vacuumdb', '-a', '--freeze' ], 'vacuum all databases'); +$node->stop; + +# Step 3. This finally gets us to UINT32_MAX. +command_ok( + [ 'pg_resetwal', '-m', '4294967295,2984247296', $node->data_dir ], + 'approaching the mxid limit'); + +print ">>> pg_controldata: \n"; +print_controldata_info($node); + +# The last step advances nextMulti to value that's not at the beginning of SLRU segment, +# Postgres expects the segment file to already exit. Create it. +my $offsets_seg = $node->data_dir . '/pg_multixact/offsets/FFFF'; +open my $fh1, '>', $offsets_seg or BAIL_OUT($!); +binmode $fh1; +print $fh1 pack("x[262144]"); +close $fh1; + + +$node->start; +create_mxid($node); +$node->command_ok([ 'vacuumdb', '-a', '--freeze' ], 'vacuum all databases'); +is($node->safe_psql('postgres', qq(TABLE test_table;)), + '1|a', + 'check table contents'); +$node->stop; + +ok( !$node->log_contains("wraparound protections are disabled"), + "check that log doesn't contain 'wraparound protections are disabled'"); + +ok( !$node->log_contains("cannot truncate up to MultiXact"), + "check that log doesn't contain 'cannot truncate up to MultiXact'"); + +ok( !$node->log_contains("skipping truncation"), + "check that log doesn't contain 'skipping truncation'"); + +done_testing(); -- 2.47.3