From: Heikki Linnakangas Date: Tue, 29 Jul 2014 07:33:15 +0000 (+0300) Subject: Treat 2PC commit/abort the same as regular xacts in recovery. X-Git-Tag: REL9_0_19~104 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=80498396139c21e95e6912d33d54c15e789b0ec4;p=postgresql 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 d19bd29f07aef9e508ff047d128a4046cc8bc1e2, 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). --- diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 4886e08d63..e9746f4b17 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5520,6 +5520,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) bool stopsHere; uint8 record_info; TimestampTz recordXtime; + TransactionId recordXid; /* We only consider stopping at COMMIT or ABORT records */ if (record->xl_rmid != RM_XACT_ID) @@ -5531,6 +5532,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record); recordXtime = recordXactCommitData->xact_time; + recordXid = record->xl_xid; + } + else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT_PREPARED) + { + xl_xact_commit_prepared *recordXactCommitData; + + recordXactCommitData = (xl_xact_commit_prepared *) XLogRecGetData(record); + recordXtime = recordXactCommitData->crec.xact_time; + recordXid = recordXactCommitData->xid; } else if (record_info == XLOG_XACT_ABORT) { @@ -5538,6 +5548,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record); recordXtime = recordXactAbortData->xact_time; + recordXid = record->xl_xid; + } + else if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT_PREPARED) + { + xl_xact_abort_prepared *recordXactAbortData; + + recordXactAbortData = (xl_xact_abort_prepared *) XLogRecGetData(record); + recordXtime = recordXactAbortData->arec.xact_time; + recordXid = recordXactAbortData->xid; } else return false; @@ -5560,7 +5579,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) * they complete. A higher numbered xid will complete before you about * 50% of the time... */ - stopsHere = (record->xl_xid == recoveryTargetXid); + stopsHere = (recordXid == recoveryTargetXid); if (stopsHere) *includeThis = recoveryTargetInclusive; } @@ -5581,11 +5600,12 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) if (stopsHere) { - recoveryStopXid = record->xl_xid; + recoveryStopXid = recordXid; recoveryStopTime = recordXtime; recoveryStopAfter = *includeThis; - if (record_info == XLOG_XACT_COMMIT) + if (record_info == XLOG_XACT_COMMIT || + record_info == XLOG_XACT_COMMIT_PREPARED) { if (recoveryStopAfter) ereport(LOG, diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 12ec693f44..3f46a39926 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -144,8 +144,7 @@ typedef struct xl_xact_abort /* * COMMIT_PREPARED and ABORT_PREPARED are identical to COMMIT/ABORT records * except that we have to store the XID of the prepared transaction explicitly - * --- the XID in the record header will be for the transaction doing the - * COMMIT PREPARED or ABORT PREPARED command. + * --- the XID in the record header will be invalid. */ typedef struct xl_xact_commit_prepared