PATCH: Unlogged tables re-initialization tests

Started by David Steelealmost 8 years ago17 messages
#1David Steele
david@pgmasters.net
1 attachment(s)

These tests were originally included in the exclude unlogged tables
patch [1]/messages/by-id/04791bab-cb04-ba43-e9c0-664a4c1ffb2c@pgmasters.net to provide coverage for the refactoring of reinit.c.

After review we found a simpler implementation that did not require the
reinit.c refactor so I dropped the tests from that patch.

I did not include the refactor here because it's just noise now, but I
think the tests still have value.

I will add to the 2018-03 CF.

--
-David
david@pgmasters.net

[1]: /messages/by-id/04791bab-cb04-ba43-e9c0-664a4c1ffb2c@pgmasters.net

Attachments:

recovery-reinit-unlogged-test.patchtext/plain; charset=UTF-8; name=recovery-reinit-unlogged-test.patch; x-mac-creator=0; x-mac-type=0Download
diff --git a/src/test/recovery/t/014_unlogged_reinit.pl b/src/test/recovery/t/014_unlogged_reinit.pl
new file mode 100644
index 0000000000..ac2e251158
--- /dev/null
+++ b/src/test/recovery/t/014_unlogged_reinit.pl
@@ -0,0 +1,117 @@
+# Tests that unlogged tables are properly reinitialized after a crash.
+#
+# The behavior should be the same when restoring from a backup but that is not
+# tested here (yet).
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 16;
+
+# Initialize node without replication settings
+my $node = get_new_node('main');
+
+$node->init;
+$node->start;
+my $pgdata = $node->data_dir;
+
+# Create an unlogged table to test that forks other than init are not copied
+$node->safe_psql('postgres', 'CREATE UNLOGGED TABLE base_unlogged (id int)');
+
+my $baseUnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('base_unlogged')});
+
+# Make sure main and init forks exist
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+
+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.
+my $tablespaceDir = undef;
+my $ts1UnloggedPath = undef;
+
+SKIP:
+{
+	skip "symlinks not supported on Windows", 2 if ($windows_os);
+
+	# Create unlogged tables in a tablespace
+	$tablespaceDir = TestLib::tempdir . "/ts1";
+
+	mkdir($tablespaceDir)
+		or die "unable to mkdir \"$tablespaceDir\"";
+
+	$node->safe_psql('postgres',
+		"CREATE TABLESPACE ts1 LOCATION '$tablespaceDir'");
+	$node->safe_psql('postgres',
+		'CREATE UNLOGGED TABLE ts1_unlogged (id int) TABLESPACE ts1');
+
+		$ts1UnloggedPath = $node->safe_psql('postgres',
+		q{select pg_relation_filepath('ts1_unlogged')});
+
+	# Make sure main and init forks exist
+	ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace');
+	ok(-f "$pgdata/$ts1UnloggedPath", 'main fork in tablespace');
+}
+
+# Crash the postmaster
+$node->stop('immediate');
+
+# Write forks to test that they are removed during recovery
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_vm"],
+	'touch vm fork in base');
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_fsm"],
+	'touch fsm fork in base');
+
+# Remove main fork to test that it is recopied from init
+unlink("$pgdata/${baseUnloggedPath}")
+	or die "unable to remove \"${baseUnloggedPath}\"";
+
+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.
+SKIP:
+{
+	skip "symlinks not supported on Windows", 2 if ($windows_os);
+
+	# Write forks to test that they are removed by recovery
+	$node->command_ok(['touch', "$pgdata/${ts1UnloggedPath}_vm"],
+		'touch vm fork in tablespace');
+	$node->command_ok(['touch', "$pgdata/${ts1UnloggedPath}_fsm"],
+		'touch fsm fork in tablespace');
+
+	# Remove main fork to test that it is recopied from init
+	unlink("$pgdata/${ts1UnloggedPath}")
+		or die "unable to remove \"${ts1UnloggedPath}\"";
+}
+
+# Start the postmaster
+$node->start;
+
+# Check unlogged table in base
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_vm", 'vm fork not in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_fsm", 'fsm fork not in base');
+
+# Drop unlogged table
+$node->safe_psql('postgres', 'DROP TABLE base_unlogged');
+
+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.
+SKIP:
+{
+	skip "symlinks not supported on Windows", 4 if ($windows_os);
+
+	# Check unlogged table in tablespace
+	ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace');
+	ok(-f "$pgdata/$ts1UnloggedPath", 'main fork in tablespace');
+	ok(!-f "$pgdata/${ts1UnloggedPath}_vm", 'vm fork not in tablespace');
+	ok(!-f "$pgdata/${ts1UnloggedPath}_fsm", 'fsm fork not in tablespace');
+
+	# Drop unlogged table
+	$node->safe_psql('postgres', 'DROP TABLE ts1_unlogged');
+
+	# Drop tablespace
+	$node->safe_psql('postgres', 'DROP TABLESPACE ts1');
+	rmdir($tablespaceDir)
+		or die "unable to rmdir \"$tablespaceDir\"";
+}
#2Thomas Munro
thomas.munro@enterprisedb.com
In reply to: David Steele (#1)
Re: PATCH: Unlogged tables re-initialization tests

On Thu, Mar 1, 2018 at 9:24 AM, David Steele <david@pgmasters.net> wrote:

These tests were originally included in the exclude unlogged tables
patch [1] to provide coverage for the refactoring of reinit.c.

Hi David,

+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.

Could you please explain this a bit more? I don't see any symlinks
being used directly in this test. I do see CREATE TABLESPACE and that
uses symlink(), but win32_port.h converts that to "junctions",
apparently the Windows equivalent. Is there some reason that doesn't
work with this test?

If, by any chance, you are talking about whatever dark force prevents
the "tablespace" regression test from succeeding when run as a user
with administrative privileges on Windows, then I would *love* to hear
an explanation for that phenomenon and how to fix it, because it
currently prevents me from automatically testing all Commitfest
patches on the appveyor.com Windows build farm. I know that it passes
as a non-administrative user.

--
Thomas Munro
http://www.enterprisedb.com

#3David Steele
david@pgmasters.net
In reply to: Thomas Munro (#2)
Re: PATCH: Unlogged tables re-initialization tests

Hi Thomas,

[Also pulling in Michael for Windows knowledge]

On 3/1/18 12:27 AM, Thomas Munro wrote:

On Thu, Mar 1, 2018 at 9:24 AM, David Steele <david@pgmasters.net> wrote:

These tests were originally included in the exclude unlogged tables
patch [1] to provide coverage for the refactoring of reinit.c.

+# The following tests test symlinks. Windows doesn't have symlinks, so
+# skip on Windows.

Could you please explain this a bit more? I don't see any symlinks
being used directly in this test. I do see CREATE TABLESPACE and that
uses symlink(), but win32_port.h converts that to "junctions",
apparently the Windows equivalent. Is there some reason that doesn't
work with this test?

I copied this pattern from src/bin/pg_basebackup/t/010_pg_basebackup.pl
which indicates that Windows will not support these types of tests.

But your point is well-taken. No symlinks are used in this test so it
*should* work.

Michael, what do you think?

If, by any chance, you are talking about whatever dark force prevents
the "tablespace" regression test from succeeding when run as a user
with administrative privileges on Windows, then I would *love* to hear
an explanation for that phenomenon and how to fix it, because it
currently prevents me from automatically testing all Commitfest
patches on the appveyor.com Windows build farm. I know that it passes
as a non-administrative user.

Sorry, no!

--
-David
david@pgmasters.net

#4Michael Paquier
michael@paquier.xyz
In reply to: David Steele (#3)
Re: PATCH: Unlogged tables re-initialization tests

On Thu, Mar 01, 2018 at 01:12:19PM -0500, David Steele wrote:

But your point is well-taken. No symlinks are used in this test so it
*should* work.

Michael, what do you think?

Perl's symlink() does not work on Windows. It does not fly higher than
that, and that's the reason why a good chunk of the tests are skipped
for pg_basebackup. If perl was to have a version of symlink() which
works, say with junction points, or if Windows was to have a sane
symlink implementation (or with [1]https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/ -- Michael?), or if it was possible to create
junction points using an in-core implementation in perl, then those
tests could not be skipped. But it seems that none of those scenarios
have happened yet.

From what I read in your patch, it seems to me that this test should
work. If they don't for whatever reason, your patch then does not give
a correct justification explaining why they should be skipped.

[1]: https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/ -- Michael
--
Michael

#5David Steele
david@pgmasters.net
In reply to: Michael Paquier (#4)
1 attachment(s)
Re: PATCH: Unlogged tables re-initialization tests

Hi,

On 3/1/18 11:59 PM, Michael Paquier wrote:

On Thu, Mar 01, 2018 at 01:12:19PM -0500, David Steele wrote:

But your point is well-taken. No symlinks are used in this test so it
*should* work.

Michael, what do you think?

Perl's symlink() does not work on Windows. It does not fly higher than
that, and that's the reason why a good chunk of the tests are skipped
for pg_basebackup. If perl was to have a version of symlink() which
works, say with junction points, or if Windows was to have a sane
symlink implementation (or with [1]?), or if it was possible to create
junction points using an in-core implementation in perl, then those
tests could not be skipped. But it seems that none of those scenarios
have happened yet.

From what I read in your patch, it seems to me that this test should
work. If they don't for whatever reason, your patch then does not give
a correct justification explaining why they should be skipped.

Thanks for the input, Michael.

Attached is a new version that does not skip tests on Windows. I don't
have any way to test it, but if one of you do that would be much
appreciated.

Thanks!
--
-David
david@pgmasters.net

Attachments:

recovery-reinit-unlogged-test-v2.patchtext/plain; charset=UTF-8; name=recovery-reinit-unlogged-test-v2.patch; x-mac-creator=0; x-mac-type=0Download
From 04550fb532dc4d3894a92d397f6303fddcaf75b2 Mon Sep 17 00:00:00 2001
From: David Steele <david@pgmasters.net>
Date: Mon, 5 Mar 2018 12:21:53 -0500
Subject: [PATCH 1/1] Reinit.c regression tests.

Add regression tests for reinit.c.

These were originally written for a patch that refactored reinit.c.  That refactor ended up not being needed, but it seems like a good idea to add the tests.
---
 src/test/recovery/t/014_unlogged_reinit.pl | 96 ++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)
 create mode 100644 src/test/recovery/t/014_unlogged_reinit.pl

diff --git a/src/test/recovery/t/014_unlogged_reinit.pl b/src/test/recovery/t/014_unlogged_reinit.pl
new file mode 100644
index 0000000000..290196291f
--- /dev/null
+++ b/src/test/recovery/t/014_unlogged_reinit.pl
@@ -0,0 +1,96 @@
+# Tests that unlogged tables are properly reinitialized after a crash.
+#
+# The behavior should be the same when restoring from a backup but that is not
+# tested here (yet).
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 16;
+
+# Initialize node without replication settings
+my $node = get_new_node('main');
+
+$node->init;
+$node->start;
+my $pgdata = $node->data_dir;
+
+# Create an unlogged table to test that forks other than init are not copied
+$node->safe_psql('postgres', 'CREATE UNLOGGED TABLE base_unlogged (id int)');
+
+my $baseUnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('base_unlogged')});
+
+# Make sure main and init forks exist
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+
+# Create unlogged tables in a tablespace
+my $tablespaceDir = undef;
+my $ts1UnloggedPath = undef;
+
+$tablespaceDir = TestLib::tempdir . "/ts1";
+
+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";
+
+$node->safe_psql('postgres',
+	"CREATE TABLESPACE ts1 LOCATION '$tablespaceDir'");
+$node->safe_psql('postgres',
+	'CREATE UNLOGGED TABLE ts1_unlogged (id int) TABLESPACE ts1');
+
+	$ts1UnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('ts1_unlogged')});
+
+# Make sure main and init forks exist
+ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace');
+ok(-f "$pgdata/$ts1UnloggedPath", 'main fork in tablespace');
+
+# Crash the postmaster
+$node->stop('immediate');
+
+# Write forks to test that they are removed during recovery
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_vm"],
+	'touch vm fork in base');
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_fsm"],
+	'touch fsm fork in base');
+
+# Remove main fork to test that it is recopied from init
+unlink("$pgdata/${baseUnloggedPath}")
+	or die "unable to remove \"${baseUnloggedPath}\"";
+
+# Write forks to test that they are removed by recovery
+$node->command_ok(['touch', "$pgdata/${ts1UnloggedPath}_vm"],
+	'touch vm fork in tablespace');
+$node->command_ok(['touch', "$pgdata/${ts1UnloggedPath}_fsm"],
+	'touch fsm fork in tablespace');
+
+# Remove main fork to test that it is recopied from init
+unlink("$pgdata/${ts1UnloggedPath}")
+	or die "unable to remove \"${ts1UnloggedPath}\"";
+
+# Start the postmaster
+$node->start;
+
+# Check unlogged table in base
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_vm", 'vm fork not in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_fsm", 'fsm fork not in base');
+
+# Drop unlogged table
+$node->safe_psql('postgres', 'DROP TABLE base_unlogged');
+
+# Check unlogged table in tablespace
+ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace');
+ok(-f "$pgdata/$ts1UnloggedPath", 'main fork in tablespace');
+ok(!-f "$pgdata/${ts1UnloggedPath}_vm", 'vm fork not in tablespace');
+ok(!-f "$pgdata/${ts1UnloggedPath}_fsm", 'fsm fork not in tablespace');
+
+# Drop unlogged table
+$node->safe_psql('postgres', 'DROP TABLE ts1_unlogged');
+
+# Drop tablespace
+$node->safe_psql('postgres', 'DROP TABLESPACE ts1');
+rmdir($tablespaceDir)
+	or die "unable to rmdir \"$tablespaceDir\"";
-- 
2.14.3 (Apple Git-98)

#6Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: David Steele (#5)
Re: PATCH: Unlogged tables re-initialization tests

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

+	$ts1UnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('ts1_unlogged')});

strange indentation

+# Write forks to test that they are removed during recovery
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_vm"],
+	'touch vm fork in base');
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_fsm"],
+	'touch fsm fork in base');

These are not tests, just some prep work. So they should not use
command_ok.

It would probably also be better to avoid the Unix-specific touch
command and instead use Perl code to open and write to the files.

+# Check unlogged table in base
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_vm", 'vm fork not in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_fsm", 'fsm fork not in base');

These test names could be a bit more verbose and distinct, for example,
'main fork was recreated after restart'.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#7Michael Paquier
michael@paquier.xyz
In reply to: Peter Eisentraut (#6)
Re: PATCH: Unlogged tables re-initialization tests

On Fri, Mar 09, 2018 at 05:23:48PM -0500, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Would it be better to make this practice more uniform? From the code of
the tests:
$ git grep die -- */t/*.pl | wc -l
50
--
Michael

#8Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Michael Paquier (#7)
Re: PATCH: Unlogged tables re-initialization tests

On 3/11/18 05:11, Michael Paquier wrote:

On Fri, Mar 09, 2018 at 05:23:48PM -0500, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Would it be better to make this practice more uniform? From the code of
the tests:
$ git grep die -- */t/*.pl | wc -l
50

Yes, or maybe there is a way to "catch" die and turn it into BAIL_OUT?

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#9David Steele
david@pgmasters.net
In reply to: Peter Eisentraut (#8)
Re: PATCH: Unlogged tables re-initialization tests

On 3/12/18 11:27 AM, Peter Eisentraut wrote:

On 3/11/18 05:11, Michael Paquier wrote:

On Fri, Mar 09, 2018 at 05:23:48PM -0500, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Would it be better to make this practice more uniform? From the code of
the tests:
$ git grep die -- */t/*.pl | wc -l
50

Yes, or maybe there is a way to "catch" die and turn it into BAIL_OUT?

something like this should work:

# Convert die to BAIL_OUT
$SIG{__DIE__} = sub {BAIL_OUT(@_)};

--
-David
david@pgmasters.net

#10Noname
ilmari@ilmari.org
In reply to: David Steele (#9)
Re: PATCH: Unlogged tables re-initialization tests

David Steele <david@pgmasters.net> writes:

On 3/12/18 11:27 AM, Peter Eisentraut wrote:

On 3/11/18 05:11, Michael Paquier wrote:

On Fri, Mar 09, 2018 at 05:23:48PM -0500, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Would it be better to make this practice more uniform? From the code of
the tests:
$ git grep die -- */t/*.pl | wc -l
50

Yes, or maybe there is a way to "catch" die and turn it into BAIL_OUT?

something like this should work:

# Convert die to BAIL_OUT
$SIG{__DIE__} = sub {BAIL_OUT(@_)};

$SIG{__DIE__} gets called even for exceptions that would be caught by a
surrunding eval block, so this should at the very least be:

$SIG{__DIE__} = sub { BAIL_OUT(@_) unless $^S };

However, that is still wrong, because die() and BAIL_OUT() mean
different things: die() aborts the current test script, while BAIL_OUT()
aborts the entire 'prove' run, i.e. all subsequent scripts in the same
test directory.

- ilmari
--
- Twitter seems more influential [than blogs] in the 'gets reported in
the mainstream press' sense at least. - Matt McLeod
- That'd be because the content of a tweet is easier to condense down
to a mainstream media article. - Calle Dybedahl

#11David Steele
david@pgmasters.net
In reply to: Noname (#10)
Re: PATCH: Unlogged tables re-initialization tests

On 3/12/18 11:59 AM, Dagfinn Ilmari Mannsåker wrote:

David Steele <david@pgmasters.net> writes:

On 3/12/18 11:27 AM, Peter Eisentraut wrote:

On 3/11/18 05:11, Michael Paquier wrote:

On Fri, Mar 09, 2018 at 05:23:48PM -0500, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Would it be better to make this practice more uniform? From the code of
the tests:
$ git grep die -- */t/*.pl | wc -l
50

Yes, or maybe there is a way to "catch" die and turn it into BAIL_OUT?

something like this should work:

# Convert die to BAIL_OUT
$SIG{__DIE__} = sub {BAIL_OUT(@_)};

$SIG{__DIE__} gets called even for exceptions that would be caught by a
surrunding eval block, so this should at the very least be:

$SIG{__DIE__} = sub { BAIL_OUT(@_) unless $^S };

However, that is still wrong, because die() and BAIL_OUT() mean
different things: die() aborts the current test script, while BAIL_OUT()
aborts the entire 'prove' run, i.e. all subsequent scripts in the same
test directory.

If that's the case, do we really want to abort all subsequent test
modules if a single module fails? I'm good either way, just throwing it
out there for consideration.

--
-David
david@pgmasters.net

#12Michael Paquier
michael@paquier.xyz
In reply to: David Steele (#11)
Re: PATCH: Unlogged tables re-initialization tests

On Mon, Mar 12, 2018 at 02:33:18PM -0400, David Steele wrote:

On 3/12/18 11:59 AM, Dagfinn Ilmari Mannsåker wrote:

However, that is still wrong, because die() and BAIL_OUT() mean
different things: die() aborts the current test script, while BAIL_OUT()
aborts the entire 'prove' run, i.e. all subsequent scripts in the same
test directory.

If that's the case, do we really want to abort all subsequent test
modules if a single module fails? I'm good either way, just throwing it
out there for consideration.

I am getting that in those code paths, we want the test series to die in
a painful way which should be used for tests where things are critically
failing. In which case, as documented by Test:More we should use
BAIL_OUT. It is true that using die() has the advantage that one can
look at multiple failures in a single run when you want to look for
similar failure patterns, however it makes debugging harder if you a lot
of test scripts because it becomes harder to track which one was the
first to fail in a parallel test.

Also, if you look at all the code paths calling die() in the test suites
those refer to critical failures, which should cause an immediate stop
of the test. So my take would be to switch all die() calls to
BAIL_OUT() in order to make all this code consistent.
--
Michael

#13David Steele
david@pgmasters.net
In reply to: Peter Eisentraut (#6)
1 attachment(s)
Re: PATCH: Unlogged tables re-initialization tests

Thanks for reviewing, Peter.

On 3/9/18 5:23 PM, Peter Eisentraut wrote:

This seems like a useful test.

On 3/5/18 12:35, David Steele wrote:

+mkdir($tablespaceDir)
+	or die "unable to mkdir \"$tablespaceDir\"";

Use BAIL_OUT instead of die in tests.

Done.

+	$ts1UnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('ts1_unlogged')});

strange indentation

Sure is. Fixed.

+# Write forks to test that they are removed during recovery
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_vm"],
+	'touch vm fork in base');
+$node->command_ok(['touch', "$pgdata/${baseUnloggedPath}_fsm"],
+	'touch fsm fork in base');

These are not tests, just some prep work. So they should not use
command_ok.

Removed command_ok().

It would probably also be better to avoid the Unix-specific touch
command and instead use Perl code to open and write to the files.

Updated to use append_to_file().

+# Check unlogged table in base
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_vm", 'vm fork not in base');
+ok(!-f "$pgdata/${baseUnloggedPath}_fsm", 'fsm fork not in base');

These test names could be a bit more verbose and distinct, for example,
'main fork was recreated after restart'.

Done.

A new patch is attached.

Thanks,
--
-David
david@pgmasters.net

Attachments:

recovery-reinit-unlogged-test-v3.patchtext/plain; charset=UTF-8; name=recovery-reinit-unlogged-test-v3.patch; x-mac-creator=0; x-mac-type=0Download
From 6e082e9db8f401d986a03d02023173e37063996b Mon Sep 17 00:00:00 2001
From: David Steele <david@pgmasters.net>
Date: Tue, 13 Mar 2018 10:06:09 -0400
Subject: [PATCH 1/1] Add regression tests for reinit.c.

These were originally written for a patch that refactored reinit.c. That refactor ended up not being needed, but it seems like a good idea to add the tests.
---
 src/test/recovery/t/014_unlogged_reinit.pl | 97 ++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 src/test/recovery/t/014_unlogged_reinit.pl

diff --git a/src/test/recovery/t/014_unlogged_reinit.pl b/src/test/recovery/t/014_unlogged_reinit.pl
new file mode 100644
index 0000000000..e87c805e46
--- /dev/null
+++ b/src/test/recovery/t/014_unlogged_reinit.pl
@@ -0,0 +1,97 @@
+# Tests that unlogged tables are properly reinitialized after a crash.
+#
+# The behavior should be the same when restoring from a backup but that is not
+# tested here.
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 12;
+
+# Initialize node without replication settings
+my $node = get_new_node('main');
+
+$node->init;
+$node->start;
+my $pgdata = $node->data_dir;
+
+# Create an unlogged table to test that forks other than init are not copied
+$node->safe_psql('postgres', 'CREATE UNLOGGED TABLE base_unlogged (id int)');
+
+my $baseUnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('base_unlogged')});
+
+# Make sure main and init forks exist
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base');
+
+# Create unlogged tables in a tablespace
+my $tablespaceDir = undef;
+my $ts1UnloggedPath = undef;
+
+$tablespaceDir = TestLib::tempdir . "/ts1";
+
+mkdir($tablespaceDir)
+	or BAIL_OUT("unable to mkdir '$tablespaceDir'");
+
+$node->safe_psql('postgres',
+	"CREATE TABLESPACE ts1 LOCATION '$tablespaceDir'");
+$node->safe_psql('postgres',
+	'CREATE UNLOGGED TABLE ts1_unlogged (id int) TABLESPACE ts1');
+
+$ts1UnloggedPath = $node->safe_psql('postgres',
+	q{select pg_relation_filepath('ts1_unlogged')});
+
+# Make sure main and init forks exist
+ok(-f "$pgdata/${ts1UnloggedPath}_init", 'init fork in tablespace');
+ok(-f "$pgdata/$ts1UnloggedPath", 'main fork in tablespace');
+
+# Crash the postmaster
+$node->stop('immediate');
+
+# Write forks to test that they are removed during recovery
+append_to_file("$pgdata/${baseUnloggedPath}_vm", 'TEST_VM');
+append_to_file("$pgdata/${baseUnloggedPath}_fsm", 'TEST_FSM');
+
+# Remove main fork to test that it is recopied from init
+unlink("$pgdata/${baseUnloggedPath}")
+	or BAIL_OUT("unable to remove '${baseUnloggedPath}'");
+
+# Write forks to test that they are removed by recovery
+append_to_file("$pgdata/${ts1UnloggedPath}_vm", 'TEST_VM');
+append_to_file("$pgdata/${ts1UnloggedPath}_fsm", 'TEST_FSM');
+
+# Remove main fork to test that it is recopied from init
+unlink("$pgdata/${ts1UnloggedPath}")
+	or BAIL_OUT("unable to remove '${ts1UnloggedPath}'");
+
+# Start the postmaster
+$node->start;
+
+# Check unlogged table in base
+ok(-f "$pgdata/${baseUnloggedPath}_init", 'init fork still exists in base');
+ok(-f "$pgdata/$baseUnloggedPath", 'main fork in base recreated at startup');
+ok(!-f "$pgdata/${baseUnloggedPath}_vm", 'vm fork in base removed at startup');
+ok(!-f "$pgdata/${baseUnloggedPath}_fsm",
+	'fsm fork in base removed at startup');
+
+# Drop unlogged table
+$node->safe_psql('postgres', 'DROP TABLE base_unlogged');
+
+# Check unlogged table in tablespace
+ok(-f "$pgdata/${ts1UnloggedPath}_init",
+	'init fork still exists in tablespace');
+ok(-f "$pgdata/$ts1UnloggedPath",
+	'main fork in tablspace recreated at startup');
+ok(!-f "$pgdata/${ts1UnloggedPath}_vm",
+	'vm fork in tablespace removed at startup');
+ok(!-f "$pgdata/${ts1UnloggedPath}_fsm",
+	'fsm fork in tablespace removed at startup');
+
+# Drop unlogged table
+$node->safe_psql('postgres', 'DROP TABLE ts1_unlogged');
+
+# Drop tablespace
+$node->safe_psql('postgres', 'DROP TABLESPACE ts1');
+rmdir($tablespaceDir)
+	or BAIL_OUT("unable to rmdir '$tablespaceDir'");
-- 
2.14.3 (Apple Git-98)

#14Alvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Noname (#10)
Re: PATCH: Unlogged tables re-initialization tests

Dagfinn Ilmari Manns�ker wrote:

$SIG{__DIE__} gets called even for exceptions that would be caught by a
surrunding eval block, so this should at the very least be:

$SIG{__DIE__} = sub { BAIL_OUT(@_) unless $^S };

However, that is still wrong, because die() and BAIL_OUT() mean
different things: die() aborts the current test script, while BAIL_OUT()
aborts the entire 'prove' run, i.e. all subsequent scripts in the same
test directory.

Sounds like 'die' is the correct thing, then, and that BAIL_OUT should
be called sparingly ... for example this one in PostgresNode::start
seems like an overreaction:
BAIL_OUT("node \"$name\" is already running") if defined $self->{_pid};

Yes?

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#15Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: David Steele (#13)
Re: PATCH: Unlogged tables re-initialization tests

On 3/13/18 10:12, David Steele wrote:

A new patch is attached.

Committed.

I removed the cleanup code at the end of the test file, since that is
done automatically.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#16David Steele
david@pgmasters.net
In reply to: Peter Eisentraut (#15)
Re: PATCH: Unlogged tables re-initialization tests

On 3/14/18 9:07 AM, Peter Eisentraut wrote:

On 3/13/18 10:12, David Steele wrote:

A new patch is attached.

Committed.

Thanks, Peter!

--
-David
david@pgmasters.net

#17Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Alvaro Herrera (#14)
Re: PATCH: Unlogged tables re-initialization tests

On 3/13/18 12:46, Alvaro Herrera wrote:

Dagfinn Ilmari Mannsåker wrote:

$SIG{__DIE__} gets called even for exceptions that would be caught by a
surrunding eval block, so this should at the very least be:

$SIG{__DIE__} = sub { BAIL_OUT(@_) unless $^S };

However, that is still wrong, because die() and BAIL_OUT() mean
different things: die() aborts the current test script, while BAIL_OUT()
aborts the entire 'prove' run, i.e. all subsequent scripts in the same
test directory.

Sounds like 'die' is the correct thing, then, and that BAIL_OUT should
be called sparingly ... for example this one in PostgresNode::start
seems like an overreaction:
BAIL_OUT("node \"$name\" is already running") if defined $self->{_pid};

It does sound like BAIL_OUT should be used more sparingly. However, if
you use die, then you don't get a good error message printed, just
something like

t/020_pg_receivewal.pl ... 9/18 # Looks like you planned 18 tests but
ran 12.
# Looks like your test exited with 25 just after 12.
t/020_pg_receivewal.pl ... Dubious, test returned 25 (wstat 6400, 0x1900)
Failed 6/18 subtests

whereas with BAIL_OUT you get

t/020_pg_receivewal.pl ... 9/18 Bailout called. Further testing
stopped: foobar
FAILED--Further testing stopped: foobar

The functional difference is significant however: BAIL_OUT prevents the
030 test file to be called, die does not.

Could use more research into best practices ....

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services