]> granicus.if.org Git - postgresql/commitdiff
Improve performance of replay of AccessExclusiveLocks
authorSimon Riggs <simon@2ndQuadrant.com>
Wed, 22 Mar 2017 13:09:36 +0000 (13:09 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Wed, 22 Mar 2017 13:09:36 +0000 (13:09 +0000)
A hot standby replica keeps a list of Access Exclusive locks for a top
level transaction. These locks are released when the top level transaction
ends. Searching of this list is O(N^2), and each transaction had to pay the
price of searching this list for locks, even if it didn't take any AE
locks itself.

This patch optimizes this case by having the master server track which
transactions took AE locks, and passes that along to the standby server in
the commit/abort record. This allows the standby to only try to release
locks for transactions which actually took any, avoiding the majority of
the performance issue.

Refactor MyXactAccessedTempRel into MyXactFlags to allow minimal additional
cruft with this.

Analysis and initial patch by David Rowley
Author: David Rowley and Simon Riggs

src/backend/access/heap/heapam.c
src/backend/access/transam/twophase.c
src/backend/access/transam/xact.c
src/backend/commands/tablecmds.c
src/backend/storage/ipc/standby.c
src/include/access/xact.h

index 85261379b1fadaffa88dad0964d567e9acf589fc..b147f6482cc98bad0034c22173fb0bc76f04ff98 100644 (file)
@@ -1132,7 +1132,7 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 
        /* Make note that we've accessed a temporary relation */
        if (RelationUsesLocalBuffers(r))
-               MyXactAccessedTempRel = true;
+               MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
 
        pgstat_initstats(r);
 
@@ -1178,7 +1178,7 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 
        /* Make note that we've accessed a temporary relation */
        if (RelationUsesLocalBuffers(r))
-               MyXactAccessedTempRel = true;
+               MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
 
        pgstat_initstats(r);
 
index f09941d0ecb1958f33d51d30acd2b39b8b7d4c3d..4b4999fd7b498bd75d196d222f6501df33ed104f 100644 (file)
@@ -2065,11 +2065,15 @@ RecordTransactionCommitPrepared(TransactionId xid,
        /* See notes in RecordTransactionCommit */
        MyPgXact->delayChkpt = true;
 
-       /* Emit the XLOG commit record */
+       /*
+        * Emit the XLOG commit record. Note that we mark 2PC commits as potentially
+        * having AccessExclusiveLocks since we don't know whether or not they do.
+        */
        recptr = XactLogCommitRecord(committs,
                                                                 nchildren, children, nrels, rels,
                                                                 ninvalmsgs, invalmsgs,
                                                                 initfileinval, false,
+                                                MyXactFlags | XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK,
                                                                 xid);
 
 
@@ -2146,10 +2150,14 @@ RecordTransactionAbortPrepared(TransactionId xid,
 
        START_CRIT_SECTION();
 
-       /* Emit the XLOG abort record */
+       /*
+        * Emit the XLOG commit record. Note that we mark 2PC aborts as potentially
+        * having AccessExclusiveLocks since we don't know whether or not they do.
+        */
        recptr = XactLogAbortRecord(GetCurrentTimestamp(),
                                                                nchildren, children,
                                                                nrels, rels,
+                                                MyXactFlags | XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK,
                                                                xid);
 
        /* Always flush, since we're about to remove the 2PC state file */
index 02e0779f325bad35c066a64ed2475fac0ca25a39..c8751c697d4baa82d7e0b3f3eddbe3696ab631e6 100644 (file)
@@ -109,12 +109,13 @@ int                       nParallelCurrentXids = 0;
 TransactionId *ParallelCurrentXids;
 
 /*
- * MyXactAccessedTempRel is set when a temporary relation is accessed.
- * We don't allow PREPARE TRANSACTION in that case.  (This is global
- * so that it can be set from heapam.c.)
+ * Miscellaneous flag bits to record events which occur on the top level
+ * transaction. These flags are only persisted in MyXactFlags and are intended
+ * so we remember to do certain things later on in the transaction. This is
+ * globally accessible, so can be set from anywhere in the code that requires
+ * recording flags.
  */
-bool           MyXactAccessedTempRel = false;
-
+int  MyXactFlags;
 
 /*
  *     transaction states - transaction state from server perspective
@@ -1231,6 +1232,7 @@ RecordTransactionCommit(void)
                                                        nchildren, children, nrels, rels,
                                                        nmsgs, invalMessages,
                                                        RelcacheInitFileInval, forceSyncCommit,
+                                                       MyXactFlags,
                                                        InvalidTransactionId /* plain commit */ );
 
                if (replorigin)
@@ -1583,7 +1585,7 @@ RecordTransactionAbort(bool isSubXact)
        XactLogAbortRecord(xact_time,
                                           nchildren, children,
                                           nrels, rels,
-                                          InvalidTransactionId);
+                                          MyXactFlags, InvalidTransactionId);
 
        /*
         * Report the latest async abort LSN, so that the WAL writer knows to
@@ -1845,7 +1847,7 @@ StartTransaction(void)
        XactDeferrable = DefaultXactDeferrable;
        XactIsoLevel = DefaultXactIsoLevel;
        forceSyncCommit = false;
-       MyXactAccessedTempRel = false;
+       MyXactFlags = 0;
 
        /*
         * reinitialize within-transaction counters
@@ -2260,7 +2262,7 @@ PrepareTransaction(void)
         * cases, such as a temp table created and dropped all within the
         * transaction.  That seems to require much more bookkeeping though.
         */
-       if (MyXactAccessedTempRel)
+       if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot PREPARE a transaction that has operated on temporary tables")));
@@ -5108,7 +5110,7 @@ XactLogCommitRecord(TimestampTz commit_time,
                                        int nrels, RelFileNode *rels,
                                        int nmsgs, SharedInvalidationMessage *msgs,
                                        bool relcacheInval, bool forceSync,
-                                       TransactionId twophase_xid)
+                                       int xactflags, TransactionId twophase_xid)
 {
        xl_xact_commit xlrec;
        xl_xact_xinfo xl_xinfo;
@@ -5139,6 +5141,8 @@ XactLogCommitRecord(TimestampTz commit_time,
                xl_xinfo.xinfo |= XACT_COMPLETION_UPDATE_RELCACHE_FILE;
        if (forceSyncCommit)
                xl_xinfo.xinfo |= XACT_COMPLETION_FORCE_SYNC_COMMIT;
+       if ((xactflags & XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK))
+               xl_xinfo.xinfo |= XACT_XINFO_HAS_AE_LOCKS;
 
        /*
         * Check if the caller would like to ask standbys for immediate feedback
@@ -5251,7 +5255,7 @@ XLogRecPtr
 XactLogAbortRecord(TimestampTz abort_time,
                                   int nsubxacts, TransactionId *subxacts,
                                   int nrels, RelFileNode *rels,
-                                  TransactionId twophase_xid)
+                                  int xactflags, TransactionId twophase_xid)
 {
        xl_xact_abort xlrec;
        xl_xact_xinfo xl_xinfo;
@@ -5276,6 +5280,9 @@ XactLogAbortRecord(TimestampTz abort_time,
 
        xlrec.xact_time = abort_time;
 
+       if ((xactflags & XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK))
+               xl_xinfo.xinfo |= XACT_XINFO_HAS_AE_LOCKS;
+
        if (nsubxacts > 0)
        {
                xl_xinfo.xinfo |= XACT_XINFO_HAS_SUBXACTS;
@@ -5427,7 +5434,8 @@ xact_redo_commit(xl_xact_parsed_commit *parsed,
                 * via their top-level xid only, so no need to provide subxact list,
                 * which will save time when replaying commits.
                 */
-               StandbyReleaseLockTree(xid, 0, NULL);
+               if (parsed->xinfo & XACT_XINFO_HAS_AE_LOCKS)
+                       StandbyReleaseLockTree(xid, 0, NULL);
        }
 
        if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
@@ -5563,7 +5571,8 @@ xact_redo_abort(xl_xact_parsed_abort *parsed, TransactionId xid)
                /*
                 * Release locks, if any. There are no invalidations to send.
                 */
-               StandbyReleaseLockTree(xid, parsed->nsubxacts, parsed->subxacts);
+               if (parsed->xinfo & XACT_XINFO_HAS_AE_LOCKS)
+                       StandbyReleaseLockTree(xid, parsed->nsubxacts, parsed->subxacts);
        }
 
        /* Make sure files supposed to be dropped are dropped */
index 86329e5f9f29931f5f9af046c1901246565bab20..3b28e8c34f633965920118a73fb5b2f5a026c45b 100644 (file)
@@ -12471,7 +12471,7 @@ PreCommit_on_commit_actions(void)
                                 * relations, we can skip truncating ON COMMIT DELETE ROWS
                                 * tables, as they must still be empty.
                                 */
-                               if (MyXactAccessedTempRel)
+                               if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
                                        oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
                                break;
                        case ONCOMMIT_DROP:
index 62590707229884a39347fe846fd30b61e782bb95..7be8fd4a784b416556f60ece33e823e35931ac9c 100644 (file)
@@ -1063,6 +1063,7 @@ LogAccessExclusiveLock(Oid dbOid, Oid relOid)
        xlrec.relOid = relOid;
 
        LogAccessExclusiveLocks(1, &xlrec);
+       MyXactFlags |= XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK;
 }
 
 /*
index e7d11913d137d7772d9380ed29e743a7382f1d0a..5b37c0584278ed42e96a696441bade8b7ef07cdc 100644 (file)
@@ -71,8 +71,27 @@ typedef enum
 /* Synchronous commit level */
 extern int     synchronous_commit;
 
-/* Kluge for 2PC support */
-extern bool MyXactAccessedTempRel;
+/*
+ * Miscellaneous flag bits to record events which occur on the top level
+ * transaction. These flags are only persisted in MyXactFlags and are intended
+ * so we remember to do certain things later in the transaction. This is
+ * globally accessible, so can be set from anywhere in the code which requires
+ * recording flags.
+ */
+extern int  MyXactFlags;
+
+/*
+ * XACT_FLAGS_ACCESSEDTEMPREL - set when a temporary relation is accessed. We
+ * don't allow PREPARE TRANSACTION in that case.
+ */
+#define XACT_FLAGS_ACCESSEDTEMPREL                             (1U << 0)
+
+/*
+ * XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK - records whether the top level xact
+ * logged any Access Exclusive Locks.
+ */
+#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK (1U << 1)
+
 
 /*
  *     start- and end-of-transaction callbacks for dynamically loaded modules
@@ -137,6 +156,7 @@ typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
 #define XACT_XINFO_HAS_INVALS                  (1U << 3)
 #define XACT_XINFO_HAS_TWOPHASE                        (1U << 4)
 #define XACT_XINFO_HAS_ORIGIN                  (1U << 5)
+#define XACT_XINFO_HAS_AE_LOCKS                        (1U << 6)
 
 /*
  * Also stored in xinfo, these indicating a variety of additional actions that
@@ -364,12 +384,13 @@ extern XLogRecPtr XactLogCommitRecord(TimestampTz commit_time,
                                        int nrels, RelFileNode *rels,
                                        int nmsgs, SharedInvalidationMessage *msgs,
                                        bool relcacheInval, bool forceSync,
+                                       int xactflags,
                                        TransactionId twophase_xid);
 
 extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
                                   int nsubxacts, TransactionId *subxacts,
                                   int nrels, RelFileNode *rels,
-                                  TransactionId twophase_xid);
+                                  int xactflags, TransactionId twophase_xid);
 extern void xact_redo(XLogReaderState *record);
 
 /* xactdesc.c */