*** a/contrib/pgrowlocks/pgrowlocks.c
--- b/contrib/pgrowlocks/pgrowlocks.c
***************
*** 168,174 **** pgrowlocks(PG_FUNCTION_ARGS)
  
  				allow_old = !(infomask & HEAP_LOCK_MASK) &&
  					(infomask & HEAP_XMAX_LOCK_ONLY);
! 				nmembers = GetMultiXactIdMembers(xmax, &members, allow_old);
  				if (nmembers == -1)
  				{
  					values[Atnum_xids] = "{0}";
--- 168,175 ----
  
  				allow_old = !(infomask & HEAP_LOCK_MASK) &&
  					(infomask & HEAP_XMAX_LOCK_ONLY);
! 				nmembers = GetMultiXactIdMembers(xmax, &members, allow_old,
! 												 false);
  				if (nmembers == -1)
  				{
  					values[Atnum_xids] = "{0}";
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 3987,3993 **** l3:
  			 * the case, HeapTupleSatisfiesUpdate would have returned
  			 * MayBeUpdated and we wouldn't be here.
  			 */
! 			nmembers = GetMultiXactIdMembers(xwait, &members, false);
  
  			for (i = 0; i < nmembers; i++)
  			{
--- 3987,3995 ----
  			 * the case, HeapTupleSatisfiesUpdate would have returned
  			 * MayBeUpdated and we wouldn't be here.
  			 */
! 			nmembers =
! 				GetMultiXactIdMembers(xwait, &members, false,
! 									  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
  
  			for (i = 0; i < nmembers; i++)
  			{
***************
*** 4008,4014 **** l3:
  				}
  			}
  
! 			pfree(members);
  		}
  
  		/*
--- 4010,4017 ----
  				}
  			}
  
! 			if (members)
! 				pfree(members);
  		}
  
  		/*
***************
*** 4157,4163 **** l3:
  				 * been the case, HeapTupleSatisfiesUpdate would have returned
  				 * MayBeUpdated and we wouldn't be here.
  				 */
! 				nmembers = GetMultiXactIdMembers(xwait, &members, false);
  
  				if (nmembers <= 0)
  				{
--- 4160,4168 ----
  				 * been the case, HeapTupleSatisfiesUpdate would have returned
  				 * MayBeUpdated and we wouldn't be here.
  				 */
! 				nmembers =
! 					GetMultiXactIdMembers(xwait, &members, false,
! 										  HEAP_XMAX_IS_LOCKED_ONLY(infomask));
  
  				if (nmembers <= 0)
  				{
***************
*** 5217,5223 **** GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask,
  	 * We only use this in multis we just created, so they cannot be values
  	 * pre-pg_upgrade.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false);
  
  	for (i = 0; i < nmembers; i++)
  	{
--- 5222,5228 ----
  	 * We only use this in multis we just created, so they cannot be values
  	 * pre-pg_upgrade.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false, false);
  
  	for (i = 0; i < nmembers; i++)
  	{
***************
*** 5293,5299 **** MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
  	 * Since we know the LOCK_ONLY bit is not set, this cannot be a multi from
  	 * pre-pg_upgrade.
  	 */
! 	nmembers = GetMultiXactIdMembers(xmax, &members, false);
  
  	if (nmembers > 0)
  	{
--- 5298,5304 ----
  	 * Since we know the LOCK_ONLY bit is not set, this cannot be a multi from
  	 * pre-pg_upgrade.
  	 */
! 	nmembers = GetMultiXactIdMembers(xmax, &members, false, false);
  
  	if (nmembers > 0)
  	{
***************
*** 5376,5382 **** Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status,
  	int			remain = 0;
  
  	allow_old = !(infomask & HEAP_LOCK_MASK) && HEAP_XMAX_IS_LOCKED_ONLY(infomask);
! 	nmembers = GetMultiXactIdMembers(multi, &members, allow_old);
  
  	if (nmembers >= 0)
  	{
--- 5381,5388 ----
  	int			remain = 0;
  
  	allow_old = !(infomask & HEAP_LOCK_MASK) && HEAP_XMAX_IS_LOCKED_ONLY(infomask);
! 	nmembers = GetMultiXactIdMembers(multi, &members, allow_old,
! 									 HEAP_XMAX_IS_LOCKED_ONLY(infomask));
  
  	if (nmembers >= 0)
  	{
*** a/src/backend/access/transam/multixact.c
--- b/src/backend/access/transam/multixact.c
***************
*** 391,397 **** MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
  	 * caller of this function does a check that the multixact is no longer
  	 * running.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false);
  
  	if (nmembers < 0)
  	{
--- 391,397 ----
  	 * caller of this function does a check that the multixact is no longer
  	 * running.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false, false);
  
  	if (nmembers < 0)
  	{
***************
*** 489,495 **** MultiXactIdIsRunning(MultiXactId multi)
  	 * "false" here means we assume our callers have checked that the given
  	 * multi cannot possibly come from a pg_upgraded database.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false);
  
  	if (nmembers < 0)
  	{
--- 489,495 ----
  	 * "false" here means we assume our callers have checked that the given
  	 * multi cannot possibly come from a pg_upgraded database.
  	 */
! 	nmembers = GetMultiXactIdMembers(multi, &members, false, false);
  
  	if (nmembers < 0)
  	{
***************
*** 1029,1038 **** GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
   * the value currently known as the next to assign, raise an error.  Previously
   * these also returned -1, but since this can lead to the wrong visibility
   * results, it is dangerous to do that.
   */
  int
  GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
! 					  bool allow_old)
  {
  	int			pageno;
  	int			prev_pageno;
--- 1029,1043 ----
   * the value currently known as the next to assign, raise an error.  Previously
   * these also returned -1, but since this can lead to the wrong visibility
   * results, it is dangerous to do that.
+  *
+  * onlyLock must be set to true if caller is certain that the given multi
+  * is used only to lock tuples; can be false without loss of correctness,
+  * but passing a true means we can return quickly without checking for
+  * old updates.
   */
  int
  GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
! 					  bool allow_old, bool onlyLock)
  {
  	int			pageno;
  	int			prev_pageno;
***************
*** 1066,1071 **** GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
--- 1071,1089 ----
  	MultiXactIdSetOldestVisible();
  
  	/*
+ 	 * If we know the multi is used only for locking and not for updates,
+ 	 * then we can skip checking if the value is older than our oldest
+ 	 * visible multi.  It cannot possibly still be running.
+ 	 */
+ 	if (onlyLock &&
+ 		MultiXactIdPrecedes(multi, OldestVisibleMXactId[MyBackendId]))
+ 	{
+ 		debug_elog2(DEBUG2, "GetMembers: a locker-only multi is too old");
+ 		*members = NULL;
+ 		return -1;
+ 	}
+ 
+ 	/*
  	 * We check known limits on MultiXact before resorting to the SLRU area.
  	 *
  	 * An ID older than MultiXactState->oldestMultiXactId cannot possibly be
***************
*** 2507,2513 **** pg_get_multixact_members(PG_FUNCTION_ARGS)
  
  		multi = palloc(sizeof(mxact));
  		/* no need to allow for old values here */
! 		multi->nmembers = GetMultiXactIdMembers(mxid, &multi->members, false);
  		multi->iter = 0;
  
  		tupdesc = CreateTemplateTupleDesc(2, false);
--- 2525,2532 ----
  
  		multi = palloc(sizeof(mxact));
  		/* no need to allow for old values here */
! 		multi->nmembers = GetMultiXactIdMembers(mxid, &multi->members, false,
! 												false);
  		multi->iter = 0;
  
  		tupdesc = CreateTemplateTupleDesc(2, false);
*** a/src/include/access/multixact.h
--- b/src/include/access/multixact.h
***************
*** 85,91 **** extern MultiXactId ReadNextMultiXactId(void);
  extern bool MultiXactIdIsRunning(MultiXactId multi);
  extern void MultiXactIdSetOldestMember(void);
  extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
! 					  bool allow_old);
  extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
  
  extern void AtEOXact_MultiXact(void);
--- 85,91 ----
  extern bool MultiXactIdIsRunning(MultiXactId multi);
  extern void MultiXactIdSetOldestMember(void);
  extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
! 					  bool allow_old, bool lockOnly);
  extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
  
  extern void AtEOXact_MultiXact(void);
