7272#include "catalog/pg_type.h"
7373#include "commands/dbcommands.h"
7474#include "funcapi.h"
75+ #include "lib/ilist.h"
7576#include "miscadmin.h"
7677#include "pg_trace.h"
7778#include "postmaster/autovacuum.h"
@@ -261,13 +262,15 @@ static MultiXactId *OldestVisibleMXactId;
261262 */
262263typedef struct mXactCacheEnt
263264{
264- struct mXactCacheEnt * next ;
265265 MultiXactId multi ;
266266 int nmembers ;
267+ dlist_node node ;
267268 MultiXactMember members [FLEXIBLE_ARRAY_MEMBER ];
268269} mXactCacheEnt ;
269270
270- static mXactCacheEnt * MXactCache = NULL ;
271+ #define MAX_CACHE_ENTRIES 256
272+ static dlist_head MXactCache = DLIST_STATIC_INIT (MXactCache );
273+ static int MXactCacheMembers = 0 ;
271274static MemoryContext MXactContext = NULL ;
272275
273276#ifdef MULTIXACT_DEBUG
@@ -1301,16 +1304,18 @@ mxactMemberComparator(const void *arg1, const void *arg2)
13011304static MultiXactId
13021305mXactCacheGetBySet (int nmembers , MultiXactMember * members )
13031306{
1304- mXactCacheEnt * entry ;
1307+ dlist_iter iter ;
13051308
13061309 debug_elog3 (DEBUG2 , "CacheGet: looking for %s" ,
13071310 mxid_to_string (InvalidMultiXactId , nmembers , members ));
13081311
13091312 /* sort the array so comparison is easy */
13101313 qsort (members , nmembers , sizeof (MultiXactMember ), mxactMemberComparator );
13111314
1312- for ( entry = MXactCache ; entry != NULL ; entry = entry -> next )
1315+ dlist_foreach ( iter , & MXactCache )
13131316 {
1317+ mXactCacheEnt * entry = dlist_container (mXactCacheEnt , node , iter .cur );
1318+
13141319 if (entry -> nmembers != nmembers )
13151320 continue ;
13161321
@@ -1321,6 +1326,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
13211326 if (memcmp (members , entry -> members , nmembers * sizeof (MultiXactMember )) == 0 )
13221327 {
13231328 debug_elog3 (DEBUG2 , "CacheGet: found %u" , entry -> multi );
1329+ dlist_move_head (& MXactCache , iter .cur );
13241330 return entry -> multi ;
13251331 }
13261332 }
@@ -1340,12 +1346,14 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
13401346static int
13411347mXactCacheGetById (MultiXactId multi , MultiXactMember * * members )
13421348{
1343- mXactCacheEnt * entry ;
1349+ dlist_iter iter ;
13441350
13451351 debug_elog3 (DEBUG2 , "CacheGet: looking for %u" , multi );
13461352
1347- for ( entry = MXactCache ; entry != NULL ; entry = entry -> next )
1353+ dlist_foreach ( iter , & MXactCache )
13481354 {
1355+ mXactCacheEnt * entry = dlist_container (mXactCacheEnt , node , iter .cur );
1356+
13491357 if (entry -> multi == multi )
13501358 {
13511359 MultiXactMember * ptr ;
@@ -1361,6 +1369,14 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
13611369 mxid_to_string (multi ,
13621370 entry -> nmembers ,
13631371 entry -> members ));
1372+
1373+ /*
1374+ * Note we modify the list while not using a modifyable iterator.
1375+ * This is acceptable only because we exit the iteration
1376+ * immediately afterwards.
1377+ */
1378+ dlist_move_head (& MXactCache , iter .cur );
1379+
13641380 return entry -> nmembers ;
13651381 }
13661382 }
@@ -1404,8 +1420,22 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
14041420 /* mXactCacheGetBySet assumes the entries are sorted, so sort them */
14051421 qsort (entry -> members , nmembers , sizeof (MultiXactMember ), mxactMemberComparator );
14061422
1407- entry -> next = MXactCache ;
1408- MXactCache = entry ;
1423+ dlist_push_head (& MXactCache , & entry -> node );
1424+ if (MXactCacheMembers ++ >= MAX_CACHE_ENTRIES )
1425+ {
1426+ dlist_node * node ;
1427+ mXactCacheEnt * entry ;
1428+
1429+ node = dlist_tail_node (& MXactCache );
1430+ dlist_delete (node );
1431+ MXactCacheMembers -- ;
1432+
1433+ entry = dlist_container (mXactCacheEnt , node , node );
1434+ debug_elog3 (DEBUG2 , "CachePut: pruning cached multi %u" ,
1435+ entry -> multi );
1436+
1437+ pfree (entry );
1438+ }
14091439}
14101440
14111441static char *
@@ -1480,7 +1510,8 @@ AtEOXact_MultiXact(void)
14801510 * a child of TopTransactionContext, we needn't delete it explicitly.
14811511 */
14821512 MXactContext = NULL ;
1483- MXactCache = NULL ;
1513+ dlist_init (& MXactCache );
1514+ MXactCacheMembers = 0 ;
14841515}
14851516
14861517/*
@@ -1546,7 +1577,8 @@ PostPrepare_MultiXact(TransactionId xid)
15461577 * Discard the local MultiXactId cache like in AtEOX_MultiXact
15471578 */
15481579 MXactContext = NULL ;
1549- MXactCache = NULL ;
1580+ dlist_init (& MXactCache );
1581+ MXactCacheMembers = 0 ;
15501582}
15511583
15521584/*
0 commit comments