]> granicus.if.org Git - postgresql/commitdiff
Treat 2PC commit/abort the same as regular xacts in recovery.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 29 Jul 2014 07:33:15 +0000 (10:33 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Tue, 29 Jul 2014 08:57:39 +0000 (11:57 +0300)
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).

src/backend/access/transam/xlog.c
src/include/access/xact.h

index 609ad2e07bc153d53b83e293e6d6ae605969fbcb..040205760f711de4c36f66752ad96c4faaf94dff 100644 (file)
@@ -5431,11 +5431,21 @@ getRecordTimestamp(XLogRecord *record, TimestampTz *recordXtime)
                *recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time;
                return true;
        }
+       if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_COMMIT_PREPARED)
+       {
+               *recordXtime = ((xl_xact_commit_prepared *) XLogRecGetData(record))->crec.xact_time;
+               return true;
+       }
        if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT)
        {
                *recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time;
                return true;
        }
+       if (record->xl_rmid == RM_XACT_ID && record_info == XLOG_XACT_ABORT_PREPARED)
+       {
+               *recordXtime = ((xl_xact_abort_prepared *) XLogRecGetData(record))->arec.xact_time;
+               return true;
+       }
        return false;
 }
 
@@ -5454,6 +5464,7 @@ recoveryStopsBefore(XLogRecord *record)
        uint8           record_info;
        bool            isCommit;
        TimestampTz recordXtime = 0;
+       TransactionId recordXid;
 
        /* Check if we should stop as soon as reaching consistency */
        if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency)
@@ -5472,10 +5483,27 @@ recoveryStopsBefore(XLogRecord *record)
        if (record->xl_rmid != RM_XACT_ID)
                return false;
        record_info = record->xl_info & ~XLR_INFO_MASK;
+
        if (record_info == XLOG_XACT_COMMIT_COMPACT || record_info == XLOG_XACT_COMMIT)
+       {
+               isCommit = true;
+               recordXid = record->xl_xid;
+       }
+       if (record_info == XLOG_XACT_COMMIT_PREPARED)
+       {
                isCommit = true;
+               recordXid = ((xl_xact_commit_prepared *) XLogRecGetData(record))->xid;
+       }
        else if (record_info == XLOG_XACT_ABORT)
+       {
+               isCommit = false;
+               recordXid = record->xl_xid;
+       }
+       else if (record_info == XLOG_XACT_ABORT_PREPARED)
+       {
                isCommit = false;
+               recordXid = ((xl_xact_abort_prepared *) XLogRecGetData(record))->xid;
+       }
        else
                return false;
 
@@ -5490,7 +5518,7 @@ recoveryStopsBefore(XLogRecord *record)
                 * they complete. A higher numbered xid will complete before you about
                 * 50% of the time...
                 */
-               stopsHere = (record->xl_xid == recoveryTargetXid);
+               stopsHere = (recordXid == recoveryTargetXid);
        }
 
        if (recoveryTarget == RECOVERY_TARGET_TIME &&
@@ -5510,7 +5538,7 @@ recoveryStopsBefore(XLogRecord *record)
        if (stopsHere)
        {
                recoveryStopAfter = false;
-               recoveryStopXid = record->xl_xid;
+               recoveryStopXid = recordXid;
                recoveryStopTime = recordXtime;
                recoveryStopName[0] = '\0';
 
@@ -5576,12 +5604,24 @@ recoveryStopsAfter(XLogRecord *record)
        if (record->xl_rmid == RM_XACT_ID &&
                (record_info == XLOG_XACT_COMMIT_COMPACT ||
                 record_info == XLOG_XACT_COMMIT ||
-                record_info == XLOG_XACT_ABORT))
+                record_info == XLOG_XACT_COMMIT_PREPARED ||
+                record_info == XLOG_XACT_ABORT ||
+                record_info == XLOG_XACT_ABORT_PREPARED))
        {
+               TransactionId recordXid;
+
                /* Update the last applied transaction timestamp */
                if (getRecordTimestamp(record, &recordXtime))
                        SetLatestXTime(recordXtime);
 
+               /* Extract the XID of the committed/aborted transaction */
+               if (record_info == XLOG_XACT_COMMIT_PREPARED)
+                       recordXid = ((xl_xact_commit_prepared *) XLogRecGetData(record))->xid;
+               else if (record_info == XLOG_XACT_ABORT_PREPARED)
+                       recordXid = ((xl_xact_abort_prepared *) XLogRecGetData(record))->xid;
+               else
+                       recordXid = record->xl_xid;
+
                /*
                 * There can be only one transaction end record with this exact
                 * transactionid
@@ -5592,21 +5632,24 @@ recoveryStopsAfter(XLogRecord *record)
                 * 50% of the time...
                 */
                if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive &&
-                       record->xl_xid == recoveryTargetXid)
+                       recordXid == recoveryTargetXid)
                {
                        recoveryStopAfter = true;
-                       recoveryStopXid = record->xl_xid;
+                       recoveryStopXid = recordXid;
                        recoveryStopTime = recordXtime;
                        recoveryStopName[0] = '\0';
 
-                       if (record_info == XLOG_XACT_COMMIT_COMPACT || record_info == XLOG_XACT_COMMIT)
+                       if (record_info == XLOG_XACT_COMMIT_COMPACT ||
+                               record_info == XLOG_XACT_COMMIT ||
+                               record_info == XLOG_XACT_COMMIT_PREPARED)
                        {
                                ereport(LOG,
                                                (errmsg("recovery stopping after commit of transaction %u, time %s",
                                                                recoveryStopXid,
                                                                timestamptz_to_str(recoveryStopTime))));
                        }
-                       else if (record_info == XLOG_XACT_ABORT)
+                       else if (record_info == XLOG_XACT_ABORT ||
+                                        record_info == XLOG_XACT_ABORT_PREPARED)
                        {
                                ereport(LOG,
                                                (errmsg("recovery stopping after abort of transaction %u, time %s",
@@ -5719,7 +5762,8 @@ recoveryApplyDelay(XLogRecord *record)
        record_info = record->xl_info & ~XLR_INFO_MASK;
        if (!(record->xl_rmid == RM_XACT_ID &&
                  (record_info == XLOG_XACT_COMMIT_COMPACT ||
-                  record_info == XLOG_XACT_COMMIT)))
+                  record_info == XLOG_XACT_COMMIT ||
+                  record_info == XLOG_XACT_COMMIT_PREPARED)))
                return false;
 
        if (!getRecordTimestamp(record, &xtime))
index 634f5b2480cfb1aaa77c053d18bde4df88c25b7d..9c2420265ceb7560fc12057d5bcfe05942f7a219 100644 (file)
@@ -180,8 +180,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