]> granicus.if.org Git - postgresql/commitdiff
Avoid setup work for invalidation messages at start-of-(sub)xact.
authorRobert Haas <rhaas@postgresql.org>
Wed, 29 Oct 2014 16:35:19 +0000 (12:35 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 29 Oct 2014 16:35:19 +0000 (12:35 -0400)
Instead of initializing a new TransInvalidationInfo for every
transaction or subtransaction, we can just do it for those
transactions or subtransactions that actually need to queue
invalidation messages.  That also avoids needing to free those
entries at the end of a transaction or subtransaction that does
not generate any invalidation messages, which is by far the
common case.

Patch by me.  Review by Simon Riggs and Andres Freund.

src/backend/access/transam/xact.c
src/backend/utils/cache/inval.c
src/include/utils/inval.h

index 5b5d31b33dc9451797968e55a0cf44f367036df5..651a5c40f465869e153b2bc125d8066439e00d19 100644 (file)
@@ -1838,7 +1838,6 @@ StartTransaction(void)
         * initialize other subsystems for new transaction
         */
        AtStart_GUC();
-       AtStart_Inval();
        AtStart_Cache();
        AfterTriggerBeginXact();
 
@@ -4151,7 +4150,6 @@ StartSubTransaction(void)
         */
        AtSubStart_Memory();
        AtSubStart_ResourceOwner();
-       AtSubStart_Inval();
        AtSubStart_Notify();
        AfterTriggerBeginSubXact();
 
index a7a768efa61bd7ad6ee1f098b6da818353cc717e..6b6c88eaf0f895223eb93fdd4ece6d4854f1e0a3 100644 (file)
@@ -693,19 +693,32 @@ AcceptInvalidationMessages(void)
 }
 
 /*
- * AtStart_Inval
- *             Initialize inval lists at start of a main transaction.
+ * PrepareInvalidationState
+ *             Initialize inval lists for the current (sub)transaction.
  */
-void
-AtStart_Inval(void)
+static void
+PrepareInvalidationState(void)
 {
-       Assert(transInvalInfo == NULL);
-       transInvalInfo = (TransInvalidationInfo *)
+       TransInvalidationInfo *myInfo;
+
+       if (transInvalInfo != NULL &&
+               transInvalInfo->my_level == GetCurrentTransactionNestLevel())
+               return;
+
+       myInfo = (TransInvalidationInfo *)
                MemoryContextAllocZero(TopTransactionContext,
                                                           sizeof(TransInvalidationInfo));
-       transInvalInfo->my_level = GetCurrentTransactionNestLevel();
-       SharedInvalidMessagesArray = NULL;
-       numSharedInvalidMessagesArray = 0;
+       myInfo->parent = transInvalInfo;
+       myInfo->my_level = GetCurrentTransactionNestLevel();
+
+       /*
+        * If there's any previous entry, this one should be for a deeper
+        * nesting level.
+        */
+       Assert(transInvalInfo == NULL ||
+               myInfo->my_level > transInvalInfo->my_level);
+
+       transInvalInfo = myInfo;
 }
 
 /*
@@ -726,24 +739,6 @@ PostPrepare_Inval(void)
        AtEOXact_Inval(false);
 }
 
-/*
- * AtSubStart_Inval
- *             Initialize inval lists at start of a subtransaction.
- */
-void
-AtSubStart_Inval(void)
-{
-       TransInvalidationInfo *myInfo;
-
-       Assert(transInvalInfo != NULL);
-       myInfo = (TransInvalidationInfo *)
-               MemoryContextAllocZero(TopTransactionContext,
-                                                          sizeof(TransInvalidationInfo));
-       myInfo->parent = transInvalInfo;
-       myInfo->my_level = GetCurrentTransactionNestLevel();
-       transInvalInfo = myInfo;
-}
-
 /*
  * Collect invalidation messages into SharedInvalidMessagesArray array.
  */
@@ -803,8 +798,16 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs,
 {
        MemoryContext oldcontext;
 
+       /* Quick exit if we haven't done anything with invalidation messages. */
+       if (transInvalInfo == NULL)
+       {
+               *RelcacheInitFileInval = false;
+               *msgs = NULL;
+               return 0;
+       }
+
        /* Must be at top of stack */
-       Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
+       Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
 
        /*
         * Relcache init file invalidation requires processing both before and
@@ -904,11 +907,15 @@ ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs,
 void
 AtEOXact_Inval(bool isCommit)
 {
+       /* Quick exit if no messages */
+       if (transInvalInfo == NULL)
+               return;
+
+       /* Must be at top of stack */
+       Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
+
        if (isCommit)
        {
-               /* Must be at top of stack */
-               Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
-
                /*
                 * Relcache init file invalidation requires processing both before and
                 * after we send the SI messages.  However, we need not do anything
@@ -926,17 +933,16 @@ AtEOXact_Inval(bool isCommit)
                if (transInvalInfo->RelcacheInitFileInval)
                        RelationCacheInitFilePostInvalidate();
        }
-       else if (transInvalInfo != NULL)
+       else
        {
-               /* Must be at top of stack */
-               Assert(transInvalInfo->parent == NULL);
-
                ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
                                                                        LocalExecuteInvalidationMessage);
        }
 
        /* Need not free anything explicitly */
        transInvalInfo = NULL;
+       SharedInvalidMessagesArray = NULL;
+       numSharedInvalidMessagesArray = 0;
 }
 
 /*
@@ -960,18 +966,38 @@ AtEOXact_Inval(bool isCommit)
 void
 AtEOSubXact_Inval(bool isCommit)
 {
-       int                     my_level = GetCurrentTransactionNestLevel();
+       int                     my_level;
        TransInvalidationInfo *myInfo = transInvalInfo;
 
-       if (isCommit)
+       /* Quick exit if no messages. */
+       if (myInfo == NULL)
+               return;
+
+       /* Also bail out quickly if messages are not for this level. */
+       my_level = GetCurrentTransactionNestLevel();
+       if (myInfo->my_level != my_level)
        {
-               /* Must be at non-top of stack */
-               Assert(myInfo != NULL && myInfo->parent != NULL);
-               Assert(myInfo->my_level == my_level);
+               Assert(myInfo->my_level < my_level);
+               return;
+       }
 
+       if (isCommit)
+       {
                /* If CurrentCmdInvalidMsgs still has anything, fix it */
                CommandEndInvalidationMessages();
 
+               /*
+                * We create invalidation stack entries lazily, so the parent might
+                * not have one.  Instead of creating one, moving all the data over,
+                * and then freeing our own, we can just adjust the level of our own
+                * entry.
+                */
+               if (myInfo->parent == NULL || myInfo->parent->my_level < my_level - 1)
+               {
+                       myInfo->my_level--;
+                       return;
+               }
+
                /* Pass up my inval messages to parent */
                AppendInvalidationMessages(&myInfo->parent->PriorCmdInvalidMsgs,
                                                                   &myInfo->PriorCmdInvalidMsgs);
@@ -986,11 +1012,8 @@ AtEOSubXact_Inval(bool isCommit)
                /* Need not free anything else explicitly */
                pfree(myInfo);
        }
-       else if (myInfo != NULL && myInfo->my_level == my_level)
+       else
        {
-               /* Must be at non-top of stack */
-               Assert(myInfo->parent != NULL);
-
                ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
                                                                        LocalExecuteInvalidationMessage);
 
@@ -1074,6 +1097,12 @@ CacheInvalidateHeapTuple(Relation relation,
        if (IsToastRelation(relation))
                return;
 
+       /*
+        * If we're not prepared to queue invalidation messages for this
+        * subtransaction level, get ready now.
+        */
+       PrepareInvalidationState();
+
        /*
         * First let the catcache do its thing
         */
@@ -1159,6 +1188,8 @@ CacheInvalidateCatalog(Oid catalogId)
 {
        Oid                     databaseId;
 
+       PrepareInvalidationState();
+
        if (IsSharedRelation(catalogId))
                databaseId = InvalidOid;
        else
@@ -1182,6 +1213,8 @@ CacheInvalidateRelcache(Relation relation)
        Oid                     databaseId;
        Oid                     relationId;
 
+       PrepareInvalidationState();
+
        relationId = RelationGetRelid(relation);
        if (relation->rd_rel->relisshared)
                databaseId = InvalidOid;
@@ -1202,6 +1235,8 @@ CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
        Oid                     databaseId;
        Oid                     relationId;
 
+       PrepareInvalidationState();
+
        relationId = HeapTupleGetOid(classTuple);
        if (classtup->relisshared)
                databaseId = InvalidOid;
@@ -1221,6 +1256,8 @@ CacheInvalidateRelcacheByRelid(Oid relid)
 {
        HeapTuple       tup;
 
+       PrepareInvalidationState();
+
        tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (!HeapTupleIsValid(tup))
                elog(ERROR, "cache lookup failed for relation %u", relid);
index 6156e0219d0449762425660ee28dd73978a8f187..9842b698d6af83ab37d34b1994903dde9aec90c0 100644 (file)
@@ -25,10 +25,6 @@ typedef void (*RelcacheCallbackFunction) (Datum arg, Oid relid);
 
 extern void AcceptInvalidationMessages(void);
 
-extern void AtStart_Inval(void);
-
-extern void AtSubStart_Inval(void);
-
 extern void AtEOXact_Inval(bool isCommit);
 
 extern void AtEOSubXact_Inval(bool isCommit);