| |
| # Copyright (c) 2021, PostgreSQL Global Development Group |
| |
| # Testing of commit timestamps preservation across restarts |
| use strict; |
| use warnings; |
| use PostgresNode; |
| use TestLib; |
| use Test::More tests => 16; |
| |
| my $node_primary = get_new_node('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; |