diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:46:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:46:48 +0000 |
commit | 311bcfc6b3acdd6fd152798c7f287ddf74fa2a98 (patch) | |
tree | 0ec307299b1dada3701e42f4ca6eda57d708261e /src/test/modules/commit_ts/t | |
parent | Initial commit. (diff) | |
download | postgresql-15-upstream.tar.xz postgresql-15-upstream.zip |
Adding upstream version 15.4.upstream/15.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/modules/commit_ts/t')
-rw-r--r-- | src/test/modules/commit_ts/t/001_base.pl | 38 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/002_standby.pl | 68 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/003_standby_2.pl | 69 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/004_restart.pl | 154 |
4 files changed, 329 insertions, 0 deletions
diff --git a/src/test/modules/commit_ts/t/001_base.pl b/src/test/modules/commit_ts/t/001_base.pl new file mode 100644 index 0000000..3f0bb9e --- /dev/null +++ b/src/test/modules/commit_ts/t/001_base.pl @@ -0,0 +1,38 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Single-node test: value can be set, and is still present after recovery + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $node = PostgreSQL::Test::Cluster->new('foxtrot'); +$node->init; +$node->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$node->start; + +# Create a table, compare "now()" to the commit TS of its xmin +$node->safe_psql('postgres', + 'create table t as select now from (select now(), pg_sleep(1)) f'); +my $true = $node->safe_psql('postgres', + 'select t.now - ts.* < \'1s\' from t, pg_class c, pg_xact_commit_timestamp(c.xmin) ts where relname = \'t\'' +); +is($true, 't', 'commit TS is set'); +my $ts = $node->safe_psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t\'' +); + +# Verify that we read the same TS after crash recovery +$node->stop('immediate'); +$node->start; + +my $recovered_ts = $node->safe_psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t\'' +); +is($recovered_ts, $ts, 'commit TS remains after crash recovery'); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/002_standby.pl b/src/test/modules/commit_ts/t/002_standby.pl new file mode 100644 index 0000000..ace3140 --- /dev/null +++ b/src/test/modules/commit_ts/t/002_standby.pl @@ -0,0 +1,68 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Test simple scenario involving a standby + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $bkplabel = 'backup'; +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(allows_streaming => 1); + +$primary->append_conf( + 'postgresql.conf', qq{ + track_commit_timestamp = on + max_wal_senders = 5 + }); +$primary->start; +$primary->backup($bkplabel); + +my $standby = PostgreSQL::Test::Cluster->new('standby'); +$standby->init_from_backup($primary, $bkplabel, has_streaming => 1); +$standby->start; + +for my $i (1 .. 10) +{ + $primary->safe_psql('postgres', "create table t$i()"); +} +my $primary_ts = $primary->safe_psql('postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't10'} +); +my $primary_lsn = + $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; + +my $standby_ts = $standby->safe_psql('postgres', + qq{select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = 't10'} +); +is($primary_ts, $standby_ts, "standby gives same value as primary"); + +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; +$primary->safe_psql('postgres', 'checkpoint'); +$primary_lsn = $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; +$standby->safe_psql('postgres', 'checkpoint'); + +# This one should raise an error now +my ($ret, $standby_ts_stdout, $standby_ts_stderr) = $standby->psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t10\'' +); +is($ret, 3, 'standby errors when primary turned feature off'); +is($standby_ts_stdout, '', + "standby gives no value when primary turned feature off"); +like( + $standby_ts_stderr, + qr/could not get commit timestamp data/, + 'expected error when primary turned feature off'); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/003_standby_2.pl b/src/test/modules/commit_ts/t/003_standby_2.pl new file mode 100644 index 0000000..16d5f13 --- /dev/null +++ b/src/test/modules/commit_ts/t/003_standby_2.pl @@ -0,0 +1,69 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Test primary/standby scenario where the track_commit_timestamp GUC is +# repeatedly toggled on and off. +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $bkplabel = 'backup'; +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(allows_streaming => 1); +$primary->append_conf( + 'postgresql.conf', qq{ + track_commit_timestamp = on + max_wal_senders = 5 + }); +$primary->start; +$primary->backup($bkplabel); + +my $standby = PostgreSQL::Test::Cluster->new('standby'); +$standby->init_from_backup($primary, $bkplabel, has_streaming => 1); +$standby->start; + +for my $i (1 .. 10) +{ + $primary->safe_psql('postgres', "create table t$i()"); +} +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; +$primary->safe_psql('postgres', 'checkpoint'); +my $primary_lsn = + $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; + +$standby->safe_psql('postgres', 'checkpoint'); +$standby->restart; + +my ($psql_ret, $standby_ts_stdout, $standby_ts_stderr) = $standby->psql( + 'postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't10'} +); +is($psql_ret, 3, 'expect error when getting commit timestamp after restart'); +is($standby_ts_stdout, '', "standby does not return a value after restart"); +like( + $standby_ts_stderr, + qr/could not get commit timestamp data/, + 'expected err msg after restart'); + +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$primary->restart; +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; + +system_or_bail('pg_ctl', '-D', $standby->data_dir, 'promote'); + +$standby->safe_psql('postgres', "create table t11()"); +my $standby_ts = $standby->safe_psql('postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't11'} +); +isnt($standby_ts, '', + "standby gives valid value ($standby_ts) after promotion"); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl new file mode 100644 index 0000000..808164c --- /dev/null +++ b/src/test/modules/commit_ts/t/004_restart.pl @@ -0,0 +1,154 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Testing of commit timestamps preservation across restarts +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node_primary = PostgreSQL::Test::Cluster->new('primary'); +$node_primary->init(allows_streaming => 1); +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$node_primary->start; + +my ($ret, $stdout, $stderr); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('0');]); +is($ret, 3, 'getting ts of InvalidTransactionId reports error'); +like( + $stderr, + qr/cannot retrieve commit timestamp for transaction/, + 'expected error from InvalidTransactionId'); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('1');]); +is($ret, 0, 'getting ts of BootstrapTransactionId succeeds'); +is($stdout, '', 'timestamp of BootstrapTransactionId is null'); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('2');]); +is($ret, 0, 'getting ts of FrozenTransactionId succeeds'); +is($stdout, '', 'timestamp of FrozenTransactionId is null'); + +# Since FirstNormalTransactionId will've occurred during initdb, long before we +# enabled commit timestamps, it'll be null since we have no cts data for it but +# cts are enabled. +is( $node_primary->safe_psql( + 'postgres', qq[SELECT pg_xact_commit_timestamp('3');]), + '', + 'committs for FirstNormalTransactionId is null'); + +$node_primary->safe_psql('postgres', + qq[CREATE TABLE committs_test(x integer, y timestamp with time zone);]); + +my $xid = $node_primary->safe_psql( + 'postgres', qq[ + BEGIN; + INSERT INTO committs_test(x, y) VALUES (1, current_timestamp); + SELECT pg_current_xact_id()::xid; + COMMIT; +]); + +my $before_restart_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +ok($before_restart_ts ne '' && $before_restart_ts ne 'null', + 'commit timestamp recorded'); + +$node_primary->stop('immediate'); +$node_primary->start; + +my $after_crash_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_crash_ts, $before_restart_ts, + 'timestamps before and after crash are equal'); + +$node_primary->stop('fast'); +$node_primary->start; + +my $after_restart_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_restart_ts, $before_restart_ts, + 'timestamps before and after restart are equal'); + +# Now disable commit timestamps +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$node_primary->stop('fast'); + +# Start the server, which generates a XLOG_PARAMETER_CHANGE record where +# the parameter change is registered. +$node_primary->start; + +# Now restart again the server so as no XLOG_PARAMETER_CHANGE record are +# replayed with the follow-up immediate shutdown. +$node_primary->restart; + +# Move commit timestamps across page boundaries. Things should still +# be able to work across restarts with those transactions committed while +# track_commit_timestamp is disabled. +$node_primary->safe_psql( + 'postgres', + qq(CREATE PROCEDURE consume_xid(cnt int) +AS \$\$ +DECLARE + i int; + BEGIN + FOR i in 1..cnt LOOP + EXECUTE 'SELECT pg_current_xact_id()'; + COMMIT; + END LOOP; + END; +\$\$ +LANGUAGE plpgsql; +)); +$node_primary->safe_psql('postgres', 'CALL consume_xid(2000)'); + +($ret, $stdout, $stderr) = $node_primary->psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($ret, 3, 'no commit timestamp from enable tx when cts disabled'); +like( + $stderr, + qr/could not get commit timestamp data/, + 'expected error from enabled tx when committs disabled'); + +# Do a tx while cts disabled +my $xid_disabled = $node_primary->safe_psql( + 'postgres', qq[ + BEGIN; + INSERT INTO committs_test(x, y) VALUES (2, current_timestamp); + SELECT pg_current_xact_id(); + COMMIT; +]); + +# Should be inaccessible +($ret, $stdout, $stderr) = $node_primary->psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid_disabled');]); +is($ret, 3, 'no commit timestamp when disabled'); +like( + $stderr, + qr/could not get commit timestamp data/, + 'expected error from disabled tx when committs disabled'); + +# Re-enable, restart and ensure we can still get the old timestamps +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); + +# An immediate shutdown is used here. At next startup recovery will +# replay transactions which committed when track_commit_timestamp was +# disabled, and the facility should be able to work properly. +$node_primary->stop('immediate'); +$node_primary->start; + +my $after_enable_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_enable_ts, '', 'timestamp of enabled tx null after re-enable'); + +my $after_enable_disabled_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid_disabled');]); +is($after_enable_disabled_ts, '', + 'timestamp of disabled tx null after re-enable'); + +$node_primary->stop; + +done_testing(); |