summaryrefslogtreecommitdiffstats
path: root/src/test/modules/commit_ts/t
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/modules/commit_ts/t')
-rw-r--r--src/test/modules/commit_ts/t/001_base.pl33
-rw-r--r--src/test/modules/commit_ts/t/002_standby.pl63
-rw-r--r--src/test/modules/commit_ts/t/003_standby_2.pl64
-rw-r--r--src/test/modules/commit_ts/t/004_restart.pl149
4 files changed, 309 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..f8d5d84
--- /dev/null
+++ b/src/test/modules/commit_ts/t/001_base.pl
@@ -0,0 +1,33 @@
+# Single-node test: value can be set, and is still present after recovery
+
+use strict;
+use warnings;
+
+use TestLib;
+use Test::More tests => 2;
+use PostgresNode;
+
+my $node = get_new_node('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');
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..f376b59
--- /dev/null
+++ b/src/test/modules/commit_ts/t/002_standby.pl
@@ -0,0 +1,63 @@
+# Test simple scenario involving a standby
+
+use strict;
+use warnings;
+
+use TestLib;
+use Test::More tests => 4;
+use PostgresNode;
+
+my $bkplabel = 'backup';
+my $master = get_new_node('master');
+$master->init(allows_streaming => 1);
+
+$master->append_conf(
+ 'postgresql.conf', qq{
+ track_commit_timestamp = on
+ max_wal_senders = 5
+ });
+$master->start;
+$master->backup($bkplabel);
+
+my $standby = get_new_node('standby');
+$standby->init_from_backup($master, $bkplabel, has_streaming => 1);
+$standby->start;
+
+for my $i (1 .. 10)
+{
+ $master->safe_psql('postgres', "create table t$i()");
+}
+my $master_ts = $master->safe_psql('postgres',
+ qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't10'}
+);
+my $master_lsn =
+ $master->safe_psql('postgres', 'select pg_current_wal_lsn()');
+$standby->poll_query_until('postgres',
+ qq{SELECT '$master_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($master_ts, $standby_ts, "standby gives same value as master");
+
+$master->append_conf('postgresql.conf', 'track_commit_timestamp = off');
+$master->restart;
+$master->safe_psql('postgres', 'checkpoint');
+$master_lsn = $master->safe_psql('postgres', 'select pg_current_wal_lsn()');
+$standby->poll_query_until('postgres',
+ qq{SELECT '$master_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 master turned feature off');
+is($standby_ts_stdout, '',
+ "standby gives no value when master turned feature off");
+like(
+ $standby_ts_stderr,
+ qr/could not get commit timestamp data/,
+ 'expected error when master turned feature off');
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..9165d50
--- /dev/null
+++ b/src/test/modules/commit_ts/t/003_standby_2.pl
@@ -0,0 +1,64 @@
+# Test master/standby scenario where the track_commit_timestamp GUC is
+# repeatedly toggled on and off.
+use strict;
+use warnings;
+
+use TestLib;
+use Test::More tests => 4;
+use PostgresNode;
+
+my $bkplabel = 'backup';
+my $master = get_new_node('master');
+$master->init(allows_streaming => 1);
+$master->append_conf(
+ 'postgresql.conf', qq{
+ track_commit_timestamp = on
+ max_wal_senders = 5
+ });
+$master->start;
+$master->backup($bkplabel);
+
+my $standby = get_new_node('standby');
+$standby->init_from_backup($master, $bkplabel, has_streaming => 1);
+$standby->start;
+
+for my $i (1 .. 10)
+{
+ $master->safe_psql('postgres', "create table t$i()");
+}
+$master->append_conf('postgresql.conf', 'track_commit_timestamp = off');
+$master->restart;
+$master->safe_psql('postgres', 'checkpoint');
+my $master_lsn =
+ $master->safe_psql('postgres', 'select pg_current_wal_lsn()');
+$standby->poll_query_until('postgres',
+ qq{SELECT '$master_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');
+
+$master->append_conf('postgresql.conf', 'track_commit_timestamp = on');
+$master->restart;
+$master->append_conf('postgresql.conf', 'track_commit_timestamp = off');
+$master->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");
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..39ca25a
--- /dev/null
+++ b/src/test/modules/commit_ts/t/004_restart.pl
@@ -0,0 +1,149 @@
+# Testing of commit timestamps preservation across restarts
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 16;
+
+my $node_master = get_new_node('master');
+$node_master->init(allows_streaming => 1);
+$node_master->append_conf('postgresql.conf', 'track_commit_timestamp = on');
+$node_master->start;
+
+my ($ret, $stdout, $stderr);
+
+($ret, $stdout, $stderr) =
+ $node_master->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_master->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_master->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_master->safe_psql(
+ 'postgres', qq[SELECT pg_xact_commit_timestamp('3');]),
+ '',
+ 'committs for FirstNormalTransactionId is null');
+
+$node_master->safe_psql('postgres',
+ qq[CREATE TABLE committs_test(x integer, y timestamp with time zone);]);
+
+my $xid = $node_master->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_master->safe_psql('postgres',
+ qq[SELECT pg_xact_commit_timestamp('$xid');]);
+ok($before_restart_ts ne '' && $before_restart_ts ne 'null',
+ 'commit timestamp recorded');
+
+$node_master->stop('immediate');
+$node_master->start;
+
+my $after_crash_ts = $node_master->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_master->stop('fast');
+$node_master->start;
+
+my $after_restart_ts = $node_master->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_master->append_conf('postgresql.conf', 'track_commit_timestamp = off');
+$node_master->stop('fast');
+
+# Start the server, which generates a XLOG_PARAMETER_CHANGE record where
+# the parameter change is registered.
+$node_master->start;
+
+# Now restart again the server so as no XLOG_PARAMETER_CHANGE record are
+# replayed with the follow-up immediate shutdown.
+$node_master->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_master->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_master->safe_psql('postgres', 'CALL consume_xid(2000)');
+
+($ret, $stdout, $stderr) = $node_master->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_master->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_master->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_master->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_master->stop('immediate');
+$node_master->start;
+
+my $after_enable_ts = $node_master->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_master->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_master->stop;