aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/multixact.c
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2013-12-13 17:16:25 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2013-12-13 17:16:25 -0300
commitd881dd6233f4eec6404f003bb08312e9e650e0e2 (patch)
tree8a7eb63b967ebb1153c14b330d37b30b6aed2d7b /src/backend/access/transam/multixact.c
parent2efc6dc256dc71ee876304b51dcad29fafbc37d3 (diff)
downloadpostgresql-d881dd6233f4eec6404f003bb08312e9e650e0e2.tar.gz
postgresql-d881dd6233f4eec6404f003bb08312e9e650e0e2.zip
Rework MultiXactId cache code
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.
Diffstat (limited to 'src/backend/access/transam/multixact.c')
-rw-r--r--src/backend/access/transam/multixact.c52
1 files changed, 42 insertions, 10 deletions
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 20814702e6d..eb77f3ef2ce 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -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;
}
/*