Skip to content

Commit b32691f

Browse files
author
Commitfest Bot
committed
[CF 7386] v5 - standby PANIC on restart after VM truncation
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/7386 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CAAKRu_Y++8RScWqKtvCQWo6ro3m=pjw-cMPG+Uho8XgR9wJBig@mail.gmail.com Author(s): shihao zhong, Rahul Yadav
2 parents f52d405 + 8c65b87 commit b32691f

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

‎contrib/pg_visibility/t/001_concurrent_transaction.pl‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
# Check that a concurrent transaction doesn't cause false negatives in
55
# pg_check_visible() function
6+
#
7+
# Also check that a standby can restart after replaying a VM clear followed by
8+
# a VM truncation.
69
use strict;
710
use warnings FATAL => 'all';
811
use PostgreSQL::Test::Cluster;
@@ -13,6 +16,11 @@
1316
# Initialize the primary node
1417
my $node = PostgreSQL::Test::Cluster->new('main');
1518
$node->init(allows_streaming => 1);
19+
$node->append_conf(
20+
'postgresql.conf', qq{
21+
autovacuum = off
22+
checkpoint_timeout = 1h
23+
});
1624
$node->start;
1725

1826
# Initialize the streaming standby
@@ -57,6 +65,47 @@
5765
# Shutdown
5866
$bsession->query_safe("COMMIT;");
5967
$bsession->quit;
68+
69+
# A WAL record clearing a VM bit registers the VM block. Make sure a standby
70+
# that crashes after the VM fork was truncated, and so replays the clear again
71+
# against a missing VM block, doesn't leave an unresolved invalid-page
72+
# reference behind: the VM truncation record must forget it.
73+
$node->safe_psql(
74+
"postgres",
75+
"CREATE TABLE vm_truncate_test AS SELECT generate_series(1, 500) i;\n"
76+
. "VACUUM (FREEZE) vm_truncate_test;");
77+
78+
# Move the standby's restart point past the VM set but before the VM clear.
79+
$node->safe_psql("postgres", "CHECKPOINT;");
80+
$node->wait_for_catchup($standby);
81+
$standby->safe_psql("postgres", "CHECKPOINT;");
82+
83+
# Without full page images, the VM clear is replayed as a plain delta.
84+
$node->append_conf('postgresql.conf', "full_page_writes = off");
85+
$node->reload;
86+
$node->poll_query_until(
87+
"postgres",
88+
q{SELECT current_setting('full_page_writes') = 'off';});
89+
90+
$node->safe_psql(
91+
"postgres",
92+
"UPDATE vm_truncate_test SET i = 0 WHERE i = 1;\n"
93+
. "SELECT pg_truncate_visibility_map('vm_truncate_test');");
94+
$node->wait_for_catchup($standby);
95+
96+
is( $standby->safe_psql(
97+
"postgres", "SELECT pg_relation_size('vm_truncate_test', 'vm');"),
98+
'0',
99+
"VM fork truncated on standby");
100+
101+
# Crash the standby; redo restarts before the VM clear.
102+
$standby->stop('immediate');
103+
my $log_offset = -s $standby->logfile;
104+
ok($standby->start(fail_ok => 1), "standby restarts after VM truncation");
105+
ok( !$standby->log_contains(
106+
qr/WAL contains references to invalid pages/, $log_offset),
107+
"no invalid-page PANIC during recovery");
108+
60109
$node->stop;
61110
$standby->stop;
62111

‎src/backend/catalog/storage.c‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,10 +1059,18 @@ smgr_redo(XLogReaderState *record)
10591059
need_fsm_vacuum = true;
10601060
}
10611061
}
1062-
if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0 &&
1063-
smgrexists(reln, VISIBILITYMAP_FORKNUM))
1062+
if ((xlrec->flags & SMGR_TRUNCATE_VM) != 0)
10641063
{
1064+
/*
1065+
* Also tell xlogutils.c about it. Do this even if the VM fork is
1066+
* already short enough (e.g. replaying again after a crash), as
1067+
* earlier records may have logged invalid pages for it.
1068+
*/
1069+
XLogTruncateRelation(xlrec->rlocator, VISIBILITYMAP_FORKNUM,
1070+
visibilitymap_truncation_length(xlrec->blkno));
1071+
10651072
blocks[nforks] = visibilitymap_prepare_truncate(rel, xlrec->blkno);
1073+
10661074
if (BlockNumberIsValid(blocks[nforks]))
10671075
{
10681076
forks[nforks] = VISIBILITYMAP_FORKNUM;

0 commit comments

Comments
 (0)