Commit d881dd62 authored by Alvaro Herrera's avatar Alvaro Herrera

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.
parent 2efc6dc2
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/dbcommands.h" #include "commands/dbcommands.h"
#include "funcapi.h" #include "funcapi.h"
#include "lib/ilist.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pg_trace.h" #include "pg_trace.h"
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
...@@ -261,13 +262,15 @@ static MultiXactId *OldestVisibleMXactId; ...@@ -261,13 +262,15 @@ static MultiXactId *OldestVisibleMXactId;
*/ */
typedef struct mXactCacheEnt typedef struct mXactCacheEnt
{ {
struct mXactCacheEnt *next;
MultiXactId multi; MultiXactId multi;
int nmembers; int nmembers;
dlist_node node;
MultiXactMember members[FLEXIBLE_ARRAY_MEMBER]; MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
} mXactCacheEnt; } 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; static MemoryContext MXactContext = NULL;
#ifdef MULTIXACT_DEBUG #ifdef MULTIXACT_DEBUG
...@@ -1301,7 +1304,7 @@ mxactMemberComparator(const void *arg1, const void *arg2) ...@@ -1301,7 +1304,7 @@ mxactMemberComparator(const void *arg1, const void *arg2)
static MultiXactId static MultiXactId
mXactCacheGetBySet(int nmembers, MultiXactMember *members) mXactCacheGetBySet(int nmembers, MultiXactMember *members)
{ {
mXactCacheEnt *entry; dlist_iter iter;
debug_elog3(DEBUG2, "CacheGet: looking for %s", debug_elog3(DEBUG2, "CacheGet: looking for %s",
mxid_to_string(InvalidMultiXactId, nmembers, members)); mxid_to_string(InvalidMultiXactId, nmembers, members));
...@@ -1309,8 +1312,10 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members) ...@@ -1309,8 +1312,10 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
/* sort the array so comparison is easy */ /* sort the array so comparison is easy */
qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator); 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) if (entry->nmembers != nmembers)
continue; continue;
...@@ -1321,6 +1326,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members) ...@@ -1321,6 +1326,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0) if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
{ {
debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi); debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
dlist_move_head(&MXactCache, iter.cur);
return entry->multi; return entry->multi;
} }
} }
...@@ -1340,12 +1346,14 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members) ...@@ -1340,12 +1346,14 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
static int static int
mXactCacheGetById(MultiXactId multi, MultiXactMember **members) mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
{ {
mXactCacheEnt *entry; dlist_iter iter;
debug_elog3(DEBUG2, "CacheGet: looking for %u", multi); 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) if (entry->multi == multi)
{ {
MultiXactMember *ptr; MultiXactMember *ptr;
...@@ -1361,6 +1369,14 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members) ...@@ -1361,6 +1369,14 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
mxid_to_string(multi, mxid_to_string(multi,
entry->nmembers, entry->nmembers,
entry->members)); 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; return entry->nmembers;
} }
} }
...@@ -1404,8 +1420,22 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members) ...@@ -1404,8 +1420,22 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
/* mXactCacheGetBySet assumes the entries are sorted, so sort them */ /* mXactCacheGetBySet assumes the entries are sorted, so sort them */
qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator); qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
entry->next = MXactCache; dlist_push_head(&MXactCache, &entry->node);
MXactCache = entry; 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 * static char *
...@@ -1480,7 +1510,8 @@ AtEOXact_MultiXact(void) ...@@ -1480,7 +1510,8 @@ AtEOXact_MultiXact(void)
* a child of TopTransactionContext, we needn't delete it explicitly. * a child of TopTransactionContext, we needn't delete it explicitly.
*/ */
MXactContext = NULL; MXactContext = NULL;
MXactCache = NULL; dlist_init(&MXactCache);
MXactCacheMembers = 0;
} }
/* /*
...@@ -1546,7 +1577,8 @@ PostPrepare_MultiXact(TransactionId xid) ...@@ -1546,7 +1577,8 @@ PostPrepare_MultiXact(TransactionId xid)
* Discard the local MultiXactId cache like in AtEOX_MultiXact * Discard the local MultiXactId cache like in AtEOX_MultiXact
*/ */
MXactContext = NULL; MXactContext = NULL;
MXactCache = NULL; dlist_init(&MXactCache);
MXactCacheMembers = 0;
} }
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment