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.
pull/6/head
Alvaro Herrera 12 years ago
parent 2efc6dc256
commit d881dd6233
  1. 52
      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;
}
/*

Loading…
Cancel
Save