The MultiXact member cache (MXactCache) is allocated under TopTransactionContext and destroyed at every transaction end, so cross-transaction lookups of the same MultiXactId always fall through to SLRU. There is a FIXME comment in multixact.c (added in commit 0ac5ad5134f2) noting this is "plain wrong now that multixact's may contain update Xids."
Since MultiXact member lists are immutable once created, cached entries stay valid regardless of transaction boundaries. The attached patch moves the cache to CacheMemoryContext so it persists for the backend session, and adds an oldestMultiXactId staleness check at lookup time to evict truncated entries. The oldestMultiXactId read is lockless; a stale read just keeps an entry slightly longer, which is harmless since the value only advances monotonically. This is consistent with the existing lockless MultiXactId read precedent in AtEOXact_MultiXact. For PostPrepare_MultiXact, explicit MemoryContextDelete is added since the context is no longer auto-destroyed with the transaction. I confirmed the improvement via pg_stat_slru: repeated GetMultiXactIdMembers calls across transactions go from SLRU hits every time to zero additional SLRU accesses after the first lookup. Patch attached.
0001-Extend-MXactCache-lifetime-from-per-transaction-to-p.patch
Description: Binary data
