]> granicus.if.org Git - postgresql/commitdiff
Preserve commit timestamps across clean restart
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 24 Oct 2016 12:27:24 +0000 (09:27 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 24 Oct 2016 12:45:48 +0000 (09:45 -0300)
An oversight in setting the boundaries of known commit timestamps during
startup caused old commit timestamps to become inaccessible after a
server restart.

Author and reporter: Julien Rouhaud
Review, test code: Craig Ringer

src/backend/access/transam/commit_ts.c
src/test/modules/commit_ts/t/004_restart.pl [new file with mode: 0644]

index a8d275f4d39035a6f62b2a0d482249b112906a30..7746578825421ac668b84a486ecbb1493e4dda05 100644 (file)
@@ -844,6 +844,8 @@ SetCommitTsLimit(TransactionId oldestXact, TransactionId newestXact)
        else
        {
                Assert(ShmemVariableCache->newestCommitTsXid == InvalidTransactionId);
+               ShmemVariableCache->oldestCommitTsXid = oldestXact;
+               ShmemVariableCache->newestCommitTsXid = newestXact;
        }
        LWLockRelease(CommitTsLock);
 }
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 (file)
index 0000000..900e9b7
--- /dev/null
@@ -0,0 +1,137 @@
+# Testing of commit timestamps preservation across clean 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', qq(
+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, 3, 'getting ts of BootstrapTransactionId reports error');
+like(
+       $stderr,
+       qr/cannot retrieve commit timestamp for transaction/,
+       'expected error from BootstrapTransactionId');
+
+($ret, $stdout, $stderr) =
+  $node_master->psql('postgres', qq[SELECT pg_xact_commit_timestamp('2');]);
+is($ret, 3, 'getting ts of FrozenTransactionId reports error');
+like(
+       $stderr,
+       qr/cannot retrieve commit timestamp for transaction/,
+       'expected error from FrozenTransactionId');
+
+# 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 txid_current();
+       COMMIT;
+]);
+
+my $before_restart_ts = $node_master->safe_psql('postgres',
+       qq[SELECT pg_xact_commit_timestamp('$xid');]);
+ok($before_restart_ts != '' && $before_restart_ts != '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', qq(
+track_commit_timestamp = off
+));
+
+$node_master->stop('fast');
+$node_master->start;
+
+($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 txid_current();
+       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', qq(
+track_commit_timestamp = on
+));
+
+$node_master->stop('fast');
+$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;