Skip to content

Commit 58626e2

Browse files
committed
Fix handling of orphaned 2PC files in the future at recovery
Before 728bd99, that has improved the support for 2PC files during recovery, the initial logic scanning files in pg_twophase was done so as files in the future of the transaction ID horizon were checked first, followed by a check if a transaction ID is aborted or committed which could involve a pg_xact lookup. After this commit, these checks have been done in reverse order. Files detected as in the future do not have a state that can be checked in pg_xact, hence this caused recovery to fail abruptly should an orphaned 2PC file in the future of the transaction ID horizon exist in pg_twophase at the beginning of recovery. A test is added to check for this scenario, using an empty 2PC with a transaction ID large enough to be in the future when running the test. This test is added in 16 and older versions for now. 17 and newer versions are impacted by a second bug caused by the addition of the epoch in the 2PC file names. An equivalent test will be added in these branches in a follow-up commit, once the second set of issues reported are fixed. Author: Vitaly Davydov, Michael Paquier Discussion: https://postgr.es/m/11e597-676ab680-8d-374f23c0@145466129 Backpatch-through: 13
1 parent c58b0c4 commit 58626e2

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

src/backend/access/transam/twophase.c

+8-8
Original file line numberDiff line numberDiff line change
@@ -2156,40 +2156,40 @@ ProcessTwoPhaseBuffer(TransactionId xid,
21562156
if (!fromdisk)
21572157
Assert(prepare_start_lsn != InvalidXLogRecPtr);
21582158

2159-
/* Already processed? */
2160-
if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid))
2159+
/* Reject XID if too new */
2160+
if (TransactionIdFollowsOrEquals(xid, origNextXid))
21612161
{
21622162
if (fromdisk)
21632163
{
21642164
ereport(WARNING,
2165-
(errmsg("removing stale two-phase state file for transaction %u",
2165+
(errmsg("removing future two-phase state file for transaction %u",
21662166
xid)));
21672167
RemoveTwoPhaseFile(xid, true);
21682168
}
21692169
else
21702170
{
21712171
ereport(WARNING,
2172-
(errmsg("removing stale two-phase state from memory for transaction %u",
2172+
(errmsg("removing future two-phase state from memory for transaction %u",
21732173
xid)));
21742174
PrepareRedoRemove(xid, true);
21752175
}
21762176
return NULL;
21772177
}
21782178

2179-
/* Reject XID if too new */
2180-
if (TransactionIdFollowsOrEquals(xid, origNextXid))
2179+
/* Already processed? */
2180+
if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid))
21812181
{
21822182
if (fromdisk)
21832183
{
21842184
ereport(WARNING,
2185-
(errmsg("removing future two-phase state file for transaction %u",
2185+
(errmsg("removing stale two-phase state file for transaction %u",
21862186
xid)));
21872187
RemoveTwoPhaseFile(xid, true);
21882188
}
21892189
else
21902190
{
21912191
ereport(WARNING,
2192-
(errmsg("removing future two-phase state from memory for transaction %u",
2192+
(errmsg("removing stale two-phase state from memory for transaction %u",
21932193
xid)));
21942194
PrepareRedoRemove(xid, true);
21952195
}

src/test/recovery/t/009_twophase.pl

+24-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
use PostgresNode;
99
use TestLib;
10-
use Test::More tests => 27;
10+
use Test::More tests => 28;
1111

1212
my $psql_out = '';
1313
my $psql_rc = '';
@@ -527,3 +527,26 @@ sub configure_and_reload
527527
is( $psql_out,
528528
qq{27|issued to paris},
529529
"Check expected t_009_tbl2 data on standby");
530+
531+
###############################################################################
532+
# Check handling of orphaned 2PC files at recovery.
533+
###############################################################################
534+
535+
$cur_primary->teardown_node;
536+
537+
# Grab location in logs of primary
538+
my $log_offset = -s $cur_primary->logfile;
539+
540+
# Create a fake file with a transaction ID large enough to be in the future,
541+
# then check that the primary is able to start and remove this file at
542+
# recovery.
543+
544+
my $future_2pc_file = $cur_primary->data_dir . '/pg_twophase/00FFFFFF';
545+
append_to_file $future_2pc_file, "";
546+
547+
$cur_primary->start;
548+
$cur_primary->log_check(
549+
"future two-phase file removed at recovery",
550+
$log_offset,
551+
log_like =>
552+
[qr/removing future two-phase state file for transaction 16777215/]);

0 commit comments

Comments
 (0)