Skip to content

Commit 3b558ac

Browse files
committed
Treat 2PC commit/abort the same as regular xacts in recovery.
There were several oversights in recovery code where COMMIT/ABORT PREPARED records were ignored: * pg_last_xact_replay_timestamp() (wasn't updated for 2PC commits) * recovery_min_apply_delay (2PC commits were applied immediately) * recovery_target_xid (recovery would not stop if the XID used 2PC) The first of those was reported by Sergiy Zuban in bug #11032, analyzed by Tom Lane and Andres Freund. The bug was always there, but was masked before commit d19bd29, because COMMIT PREPARED always created an extra regular transaction that was WAL-logged. Backpatch to all supported versions (older versions didn't have all the features and therefore didn't have all of the above bugs).
1 parent 850ebf2 commit 3b558ac

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

src/backend/access/transam/xlog.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5431,11 +5431,21 @@ getRecordTimestamp(XLogRecord *record, TimestampTz *recordXtime)
54315431
*recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time;
54325432
return true;
54335433
}
5434+
if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT_PREPARED)
5435+
{
5436+
*recordXtime = ((xl_xact_commit_prepared *) XLogRecGetData(record))->crec.xact_time;
5437+
return true;
5438+
}
54345439
if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT)
54355440
{
54365441
*recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time;
54375442
return true;
54385443
}
5444+
if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT_PREPARED)
5445+
{
5446+
*recordXtime = ((xl_xact_abort_prepared *) XLogRecGetData(record))->arec.xact_time;
5447+
return true;
5448+
}
54395449
return false;
54405450
}
54415451

@@ -5454,6 +5464,7 @@ recoveryStopsBefore(XLogRecord *record)
54545464
uint8 record_info;
54555465
bool isCommit;
54565466
TimestampTz recordXtime = 0;
5467+
TransactionId recordXid;
54575468

54585469
/* Check if we should stop as soon as reaching consistency */
54595470
if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
@@ -5472,10 +5483,27 @@ recoveryStopsBefore(XLogRecord *record)
54725483
if (record->xl_rmid != RM_XACT_ID)
54735484
return false;
54745485
record_info = record->xl_info & ~XLR_INFO_MASK;
5486+
54755487
if (record_info == XLOG_XACT_COMMIT_COMPACT || record_info == XLOG_XACT_COMMIT)
5488+
{
5489+
isCommit = true;
5490+
recordXid = record->xl_xid;
5491+
}
5492+
if (record_info == XLOG_XACT_COMMIT_PREPARED)
5493+
{
54765494
isCommit = true;
5495+
recordXid = ((xl_xact_commit_prepared *) XLogRecGetData(record))->xid;
5496+
}
54775497
else if (record_info == XLOG_XACT_ABORT)
5498+
{
5499+
isCommit = false;
5500+
recordXid = record->xl_xid;
5501+
}
5502+
else if (record_info == XLOG_XACT_ABORT_PREPARED)
5503+
{
54785504
isCommit = false;
5505+
recordXid = ((xl_xact_abort_prepared *) XLogRecGetData(record))->xid;
5506+
}
54795507
else
54805508
return false;
54815509

@@ -5490,7 +5518,7 @@ recoveryStopsBefore(XLogRecord *record)
54905518
* they complete. A higher numbered xid will complete before you about
54915519
* 50% of the time...
54925520
*/
5493-
stopsHere = (record->xl_xid == recoveryTargetXid);
5521+
stopsHere = (recordXid == recoveryTargetXid);
54945522
}
54955523

54965524
if (recoveryTarget == RECOVERY_TARGET_TIME &&
@@ -5510,7 +5538,7 @@ recoveryStopsBefore(XLogRecord *record)
55105538
if (stopsHere)
55115539
{
55125540
recoveryStopAfter = false;
5513-
recoveryStopXid = record->xl_xid;
5541+
recoveryStopXid = recordXid;
55145542
recoveryStopTime = recordXtime;
55155543
recoveryStopName[0] = '\0';
55165544

@@ -5576,12 +5604,24 @@ recoveryStopsAfter(XLogRecord *record)
55765604
if (record->xl_rmid == RM_XACT_ID &&
55775605
(record_info == XLOG_XACT_COMMIT_COMPACT ||
55785606
record_info == XLOG_XACT_COMMIT ||
5579-
record_info == XLOG_XACT_ABORT))
5607+
record_info == XLOG_XACT_COMMIT_PREPARED ||
5608+
record_info == XLOG_XACT_ABORT ||
5609+
record_info == XLOG_XACT_ABORT_PREPARED))
55805610
{
5611+
TransactionId recordXid;
5612+
55815613
/* Update the last applied transaction timestamp */
55825614
if (getRecordTimestamp(record, &recordXtime))
55835615
SetLatestXTime(recordXtime);
55845616

5617+
/* Extract the XID of the committed/aborted transaction */
5618+
if (record_info == XLOG_XACT_COMMIT_PREPARED)
5619+
recordXid = ((xl_xact_commit_prepared *) XLogRecGetData(record))->xid;
5620+
else if (record_info == XLOG_XACT_ABORT_PREPARED)
5621+
recordXid = ((xl_xact_abort_prepared *) XLogRecGetData(record))->xid;
5622+
else
5623+
recordXid = record->xl_xid;
5624+
55855625
/*
55865626
* There can be only one transaction end record with this exact
55875627
* transactionid
@@ -5592,21 +5632,24 @@ recoveryStopsAfter(XLogRecord *record)
55925632
* 50% of the time...
55935633
*/
55945634
if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
5595-
record->xl_xid == recoveryTargetXid)
5635+
recordXid == recoveryTargetXid)
55965636
{
55975637
recoveryStopAfter = true;
5598-
recoveryStopXid = record->xl_xid;
5638+
recoveryStopXid = recordXid;
55995639
recoveryStopTime = recordXtime;
56005640
recoveryStopName[0] = '\0';
56015641

5602-
if (record_info == XLOG_XACT_COMMIT_COMPACT || record_info == XLOG_XACT_COMMIT)
5642+
if (record_info == XLOG_XACT_COMMIT_COMPACT ||
5643+
record_info == XLOG_XACT_COMMIT ||
5644+
record_info == XLOG_XACT_COMMIT_PREPARED)
56035645
{
56045646
ereport(LOG,
56055647
(errmsg("recovery stopping after commit of transaction %u, time %s",
56065648
recoveryStopXid,
56075649
timestamptz_to_str(recoveryStopTime))));
56085650
}
5609-
else if (record_info == XLOG_XACT_ABORT)
5651+
else if (record_info == XLOG_XACT_ABORT ||
5652+
record_info == XLOG_XACT_ABORT_PREPARED)
56105653
{
56115654
ereport(LOG,
56125655
(errmsg("recovery stopping after abort of transaction %u, time %s",
@@ -5719,7 +5762,8 @@ recoveryApplyDelay(XLogRecord *record)
57195762
record_info = record->xl_info & ~XLR_INFO_MASK;
57205763
if (!(record->xl_rmid == RM_XACT_ID &&
57215764
(record_info == XLOG_XACT_COMMIT_COMPACT ||
5722-
record_info == XLOG_XACT_COMMIT)))
5765+
record_info == XLOG_XACT_COMMIT ||
5766+
record_info == XLOG_XACT_COMMIT_PREPARED)))
57235767
return false;
57245768

57255769
if (!getRecordTimestamp(record, &xtime))

src/include/access/xact.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,7 @@ typedef struct xl_xact_abort
180180
/*
181181
* COMMIT_PREPARED and ABORT_PREPARED are identical to COMMIT/ABORT records
182182
* except that we have to store the XID of the prepared transaction explicitly
183-
* --- the XID in the record header will be for the transaction doing the
184-
* COMMIT PREPARED or ABORT PREPARED command.
183+
* --- the XID in the record header will be invalid.
185184
*/
186185

187186
typedef struct xl_xact_commit_prepared

0 commit comments

Comments
 (0)