From 0bc00363b9b1d5ee44a0b25ed2dfc83f81e68258 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Fri, 13 Dec 2013 17:16:25 -0300 Subject: [PATCH] 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. --- src/backend/access/transam/multixact.c | 52 +++++++++++++++++++++----- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c index 20814702e6..eb77f3ef2c 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; } /* -- 2.40.0