Skip to content

Commit 1578d13

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 af9d516 commit 1578d13

File tree

2 files changed

+29
-6
lines changed

2 files changed

+29
-6
lines changed

src/backend/access/transam/xlog.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5880,6 +5880,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
58805880
bool stopsHere;
58815881
uint8 record_info;
58825882
TimestampTz recordXtime;
5883+
TransactionId recordXid;
58835884
char recordRPName[MAXFNAMELEN];
58845885

58855886
/* We only consider stopping at COMMIT, ABORT or RESTORE POINT records */
@@ -5892,27 +5893,47 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
58925893

58935894
recordXactCommitData = (xl_xact_commit_compact *) XLogRecGetData(record);
58945895
recordXtime = recordXactCommitData->xact_time;
5896+
recordXid = record->xl_xid;
58955897
}
58965898
else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT)
58975899
{
58985900
xl_xact_commit *recordXactCommitData;
58995901

59005902
recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record);
59015903
recordXtime = recordXactCommitData->xact_time;
5904+
recordXid = record->xl_xid;
5905+
}
5906+
else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT_PREPARED)
5907+
{
5908+
xl_xact_commit_prepared *recordXactCommitData;
5909+
5910+
recordXactCommitData = (xl_xact_commit_prepared *) XLogRecGetData(record);
5911+
recordXtime = recordXactCommitData->crec.xact_time;
5912+
recordXid = recordXactCommitData->xid;
59025913
}
59035914
else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT)
59045915
{
59055916
xl_xact_abort *recordXactAbortData;
59065917

59075918
recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
59085919
recordXtime = recordXactAbortData->xact_time;
5920+
recordXid = record->xl_xid;
5921+
}
5922+
else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT_PREPARED)
5923+
{
5924+
xl_xact_abort_prepared *recordXactAbortData;
5925+
5926+
recordXactAbortData = (xl_xact_abort_prepared *) XLogRecGetData(record);
5927+
recordXtime = recordXactAbortData->arec.xact_time;
5928+
recordXid = recordXactAbortData->xid;
59095929
}
59105930
else if (record->xl_rmid == RM_XLOG_ID && record_info == XLOG_RESTORE_POINT)
59115931
{
59125932
xl_restore_point *recordRestorePointData;
59135933

59145934
recordRestorePointData = (xl_restore_point *) XLogRecGetData(record);
59155935
recordXtime = recordRestorePointData->rp_time;
5936+
recordXid = InvalidTransactionId;
59165937
strlcpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN);
59175938
}
59185939
else
@@ -5941,7 +5962,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
59415962
* they complete. A higher numbered xid will complete before you about
59425963
* 50% of the time...
59435964
*/
5944-
stopsHere = (record->xl_xid == recoveryTargetXid);
5965+
stopsHere = (recordXid == recoveryTargetXid);
59455966
if (stopsHere)
59465967
*includeThis = recoveryTargetInclusive;
59475968
}
@@ -5976,11 +5997,13 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
59765997

59775998
if (stopsHere)
59785999
{
5979-
recoveryStopXid = record->xl_xid;
6000+
recoveryStopXid = recordXid;
59806001
recoveryStopTime = recordXtime;
59816002
recoveryStopAfter = *includeThis;
59826003

5983-
if (record_info == XLOG_XACT_COMMIT_COMPACT || record_info == XLOG_XACT_COMMIT)
6004+
if (record_info == XLOG_XACT_COMMIT_COMPACT ||
6005+
record_info == XLOG_XACT_COMMIT ||
6006+
record_info == XLOG_XACT_COMMIT_PREPARED)
59846007
{
59856008
if (recoveryStopAfter)
59866009
ereport(LOG,
@@ -5993,7 +6016,8 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
59936016
recoveryStopXid,
59946017
timestamptz_to_str(recoveryStopTime))));
59956018
}
5996-
else if (record_info == XLOG_XACT_ABORT)
6019+
else if (record_info == XLOG_XACT_ABORT ||
6020+
record_info == XLOG_XACT_ABORT_PREPARED)
59976021
{
59986022
if (recoveryStopAfter)
59996023
ereport(LOG,

src/include/access/xact.h

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

184183
typedef struct xl_xact_commit_prepared

0 commit comments

Comments
 (0)