]> granicus.if.org Git - postgresql/commitdiff
Rework MultiXactId cache code
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 13 Dec 2013 20:16:25 +0000 (17:16 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 13 Dec 2013 20:16:25 +0000 (17:16 -0300)
The original performs too poorly; in some scenarios it shows way too
high while profiling.  Try to make it a bit smarter to avoid excessive
cosst.  In particular, make it have a maximum size, and have entries be
sorted in LRU order; once the max size is reached, evict the oldest
entry to avoid it from growing too large.

Per complaint from Andres Freund in connection with new tuple freezing
code.

src/backend/access/transam/multixact.c

index 20814702e6d0be57530e98b0efb74b3c3e3edb5c..eb77f3ef2cea56d206433e2868922d0b19559778 100644 (file)
@@ -72,6 +72,7 @@
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "funcapi.h"
+#include "lib/ilist.h"
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "postmaster/autovacuum.h"
@@ -261,13 +262,15 @@ static MultiXactId *OldestVisibleMXactId;
  */
 typedef struct mXactCacheEnt
 {
-       struct mXactCacheEnt *next;
        MultiXactId multi;
        int                     nmembers;
+       dlist_node      node;
        MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
 } mXactCacheEnt;
 
-static mXactCacheEnt *MXactCache = NULL;
+#define MAX_CACHE_ENTRIES      256
+static dlist_head      MXactCache = DLIST_STATIC_INIT(MXactCache);
+static int                     MXactCacheMembers = 0;
 static MemoryContext MXactContext = NULL;
 
 #ifdef MULTIXACT_DEBUG
@@ -1301,7 +1304,7 @@ mxactMemberComparator(const void *arg1, const void *arg2)
 static MultiXactId
 mXactCacheGetBySet(int nmembers, MultiXactMember *members)
 {
-       mXactCacheEnt *entry;
+       dlist_iter      iter;
 
        debug_elog3(DEBUG2, "CacheGet: looking for %s",
                                mxid_to_string(InvalidMultiXactId, nmembers, members));
@@ -1309,8 +1312,10 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
        /* sort the array so comparison is easy */
        qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
 
-       for (entry = MXactCache; entry != NULL; entry = entry->next)
+       dlist_foreach(iter, &MXactCache)
        {
+               mXactCacheEnt *entry = dlist_container(mXactCacheEnt, node, iter.cur);
+
                if (entry->nmembers != nmembers)
                        continue;
 
@@ -1321,6 +1326,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
                if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
                {
                        debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
+                       dlist_move_head(&MXactCache, iter.cur);
                        return entry->multi;
                }
        }
@@ -1340,12 +1346,14 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
 static int
 mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
 {
-       mXactCacheEnt *entry;
+       dlist_iter      iter;
 
        debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
 
-       for (entry = MXactCache; entry != NULL; entry = entry->next)
+       dlist_foreach(iter, &MXactCache)
        {
+               mXactCacheEnt *entry = dlist_container(mXactCacheEnt, node, iter.cur);
+
                if (entry->multi == multi)
                {
                        MultiXactMember *ptr;
@@ -1361,6 +1369,14 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
                                                mxid_to_string(multi,
                                                                           entry->nmembers,
                                                                           entry->members));
+
+                       /*
+                        * Note we modify the list while not using a modifyable iterator.
+                        * This is acceptable only because we exit the iteration
+                        * immediately afterwards.
+                        */
+                       dlist_move_head(&MXactCache, iter.cur);
+
                        return entry->nmembers;
                }
        }
@@ -1404,8 +1420,22 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
        /* mXactCacheGetBySet assumes the entries are sorted, so sort them */
        qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
 
-       entry->next = MXactCache;
-       MXactCache = entry;
+       dlist_push_head(&MXactCache, &entry->node);
+       if (MXactCacheMembers++ >= MAX_CACHE_ENTRIES)
+       {
+               dlist_node *node;
+               mXactCacheEnt *entry;
+
+               node = dlist_tail_node(&MXactCache);
+               dlist_delete(node);
+               MXactCacheMembers--;
+
+               entry = dlist_container(mXactCacheEnt, node, node);
+               debug_elog3(DEBUG2, "CachePut: pruning cached multi %u",
+                                       entry->multi);
+
+               pfree(entry);
+       }
 }
 
 static char *
@@ -1480,7 +1510,8 @@ AtEOXact_MultiXact(void)
         * a child of TopTransactionContext, we needn't delete it explicitly.
         */
        MXactContext = NULL;
-       MXactCache = NULL;
+       dlist_init(&MXactCache);
+       MXactCacheMembers = 0;
 }
 
 /*
@@ -1546,7 +1577,8 @@ PostPrepare_MultiXact(TransactionId xid)
         * Discard the local MultiXactId cache like in AtEOX_MultiXact
         */
        MXactContext = NULL;
-       MXactCache = NULL;
+       dlist_init(&MXactCache);
+       MXactCacheMembers = 0;
 }
 
 /*