ssi-ddl-1.patch

text/plain

Filename: ssi-ddl-1.patch
Type: text/plain
Part: 0
Message: Re: SIREAD lock versus ACCESS EXCLUSIVE lock

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/catalog/index.c 8 0
src/backend/storage/lmgr/predicate.c 225 0
src/include/catalog/index.h 2 0
src/include/storage/predicate.h 1 0
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 54,59 ****
--- 54,60 ----
  #include "parser/parser.h"
  #include "storage/bufmgr.h"
  #include "storage/lmgr.h"
+ #include "storage/predicate.h"
  #include "storage/procarray.h"
  #include "storage/smgr.h"
  #include "utils/builtins.h"
***************
*** 114,120 **** static void validate_index_heapscan(Relation heapRelation,
  						IndexInfo *indexInfo,
  						Snapshot snapshot,
  						v_i_state *state);
- static Oid	IndexGetRelation(Oid indexId);
  static void SetReindexProcessing(Oid heapOid, Oid indexOid);
  static void ResetReindexProcessing(void);
  static void SetReindexPending(List *indexes);
--- 115,120 ----
***************
*** 2721,2727 **** validate_index_heapscan(Relation heapRelation,
   * IndexGetRelation: given an index's relation OID, get the OID of the
   * relation it is an index on.	Uses the system cache.
   */
! static Oid
  IndexGetRelation(Oid indexId)
  {
  	HeapTuple	tuple;
--- 2721,2727 ----
   * IndexGetRelation: given an index's relation OID, get the OID of the
   * relation it is an index on.	Uses the system cache.
   */
! Oid
  IndexGetRelation(Oid indexId)
  {
  	HeapTuple	tuple;
***************
*** 2782,2787 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2782,2793 ----
  	 */
  	CheckTableNotInUse(iRel, "REINDEX INDEX");
  
+ 	/*
+ 	 * All predicate locks on the index are about to be made invalid.
+ 	 * Promote them to relation locks on the heap.
+ 	 */
+ 	TransferPredicateLocksToHeapRelation(iRel);
+ 
  	PG_TRY();
  	{
  		/* Suppress use of the target index while rebuilding it */
*** a/src/backend/storage/lmgr/predicate.c
--- b/src/backend/storage/lmgr/predicate.c
***************
*** 185,190 ****
--- 185,191 ----
  #include "access/twophase.h"
  #include "access/twophase_rmgr.h"
  #include "access/xact.h"
+ #include "catalog/index.h"
  #include "miscadmin.h"
  #include "storage/bufmgr.h"
  #include "storage/predicate.h"
***************
*** 975,982 **** InitPredicateLocks(void)
  	bool		found;
  
  	/*
! 	 * Compute size of predicate lock target hashtable.
! 	 * Note these calculations must agree with PredicateLockShmemSize!
  	 */
  	max_table_size = NPREDICATELOCKTARGETENTS();
  
--- 976,983 ----
  	bool		found;
  
  	/*
! 	 * Compute size of predicate lock target hashtable. Note these
! 	 * calculations must agree with PredicateLockShmemSize!
  	 */
  	max_table_size = NPREDICATELOCKTARGETENTS();
  
***************
*** 1028,1035 **** InitPredicateLocks(void)
  									  hash_flags);
  
  	/*
! 	 * Compute size for serializable transaction hashtable.
! 	 * Note these calculations must agree with PredicateLockShmemSize!
  	 */
  	max_table_size = (MaxBackends + max_prepared_xacts);
  
--- 1029,1036 ----
  									  hash_flags);
  
  	/*
! 	 * Compute size for serializable transaction hashtable. Note these
! 	 * calculations must agree with PredicateLockShmemSize!
  	 */
  	max_table_size = (MaxBackends + max_prepared_xacts);
  
***************
*** 2276,2288 **** PredicateLockTupleRowVersionLink(const Relation relation,
  				newxmin;
  
  	/*
! 	 * Bail out quickly if there are no serializable transactions
! 	 * running.
  	 *
! 	 * It's safe to do this check without taking any additional
! 	 * locks. Even if a serializable transaction starts concurrently,
! 	 * we know it can't take any SIREAD locks on the modified tuple
! 	 * because the caller is holding the associated buffer page lock.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
--- 2277,2288 ----
  				newxmin;
  
  	/*
! 	 * Bail out quickly if there are no serializable transactions running.
  	 *
! 	 * It's safe to do this check without taking any additional locks. Even if
! 	 * a serializable transaction starts concurrently, we know it can't take
! 	 * any SIREAD locks on the modified tuple because the caller is holding
! 	 * the associated buffer page lock.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
***************
*** 2622,2627 **** exit:
--- 2622,2839 ----
  	return !outOfShmem;
  }
  
+ /*
+  * For all connections, transfer all predicate locks for the given relation
+  * to a single relation lock on the heap.  For a heap relation that includes
+  * all locks on indexes; for an index it is just the locks on that one
+  * index.
+  *
+  * This requires grabbing a lot of LW locks and scanning the entire lock
+  * target table for matches.  That makes this more expensive than most
+  * functions, but it will only be called for DDL type commands and there are
+  * fast returns when no serializable transactions are active or the relation
+  * is temporary.
+  *
+  * We are not using the existing TransferPredicateLocksToNewTarget because
+  * it acquires its own locks on the partitions of the two targets invovled,
+  * and we'll already be holding all partition locks.
+  *
+  * We can't throw an error from here, because the call could be from a
+  * transaction which is not serializable.
+  */
+ void
+ TransferPredicateLocksToHeapRelation(const Relation relation)
+ {
+ 	HASH_SEQ_STATUS seqstat;
+ 	PREDICATELOCKTARGET *oldtarget;
+ 	PREDICATELOCKTARGET *heaptarget;
+ 	PREDICATELOCKTARGETTAG heaptargettag;
+ 	PREDICATELOCKTAG newpredlocktag;
+ 	Oid			dbId;
+ 	Oid			indexId;
+ 	Oid			heapId;
+ 	int			i;
+ 	bool		isSingleIndex;
+ 	bool		found;
+ 	uint32		reservedtargettaghash;
+ 	uint32		heaptargettaghash;
+ 
+ 	/*
+ 	 * Bail out quickly if there are no serializable transactions running. As
+ 	 * with PredicateLockTupleRowVersionLink, it's safe to check this without
+ 	 * taking locks because the caller is holding the buffer page lock.
+ 	 */
+ 	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
+ 		return;
+ 
+ 	if (SkipSplitTracking(relation))
+ 		return;
+ 
+ 	dbId = relation->rd_node.dbNode;
+ 	if (relation->rd_index == NULL)
+ 	{
+ 		isSingleIndex = false;
+ 		indexId = InvalidOid;	/* quiet compiler warning */
+ 		heapId = relation->rd_id;
+ 	}
+ 	else
+ 	{
+ 		isSingleIndex = true;
+ 		indexId = relation->rd_id;
+ 		heapId = relation->rd_index->indrelid;
+ 	}
+ 	Assert(heapId != InvalidOid);
+ 	SET_PREDICATELOCKTARGETTAG_RELATION(heaptargettag,
+ 										dbId,
+ 										heapId);
+ 	heaptargettaghash = PredicateLockTargetTagHashCode(&heaptargettag);
+ 	heaptarget = NULL;			/* Retrieve first time needed, then keep. */
+ 
+ 	LWLockAcquire(SerializablePredicateLockListLock, LW_EXCLUSIVE);
+ 	for (i = 0; i < NUM_PREDICATELOCK_PARTITIONS; i++)
+ 		LWLockAcquire(FirstPredicateLockMgrLock + i, LW_EXCLUSIVE);
+ 	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+ 
+ 	/*
+ 	 * Remove the reserved entry to give us scratch space, so we know we'll be
+ 	 * able to create the new lock target.
+ 	 */
+ 	reservedtargettaghash = PredicateLockTargetTagHashCode(&ReservedTargetTag);
+ 	hash_search_with_hash_value(PredicateLockTargetHash,
+ 								&ReservedTargetTag,
+ 								reservedtargettaghash,
+ 								HASH_REMOVE, &found);
+ 	Assert(found);
+ 
+ 	/* Scan through PredicateLockHash and copy contents */
+ 	hash_seq_init(&seqstat, PredicateLockTargetHash);
+ 
+ 	while ((oldtarget = (PREDICATELOCKTARGET *) hash_seq_search(&seqstat)))
+ 	{
+ 		PREDICATELOCK *oldpredlock;
+ 
+ 		/*
+ 		 * Check whether this is a target which needs attention.
+ 		 */
+ 		if (GET_PREDICATELOCKTARGETTAG_DB(oldtarget->tag) != dbId)
+ 			continue;			/* wrong database */
+ 		if (isSingleIndex)
+ 		{
+ 			if (GET_PREDICATELOCKTARGETTAG_RELATION(oldtarget->tag) != indexId)
+ 				continue;		/* not the index we're looking for */
+ 		}
+ 		else
+ 		{
+ 			if (GET_PREDICATELOCKTARGETTAG_RELATION(oldtarget->tag) == heapId)
+ 			{
+ 				if (GET_PREDICATELOCKTARGETTAG_TYPE(oldtarget->tag) == PREDLOCKTAG_RELATION)
+ 					continue;	/* already the right lock */
+ 			}
+ 			else
+ 			{
+ 				/* This is an index.  Is it for the right heap? */
+ 				if (IndexGetRelation(GET_PREDICATELOCKTARGETTAG_RELATION(oldtarget->tag))
+ 					!= heapId)
+ 					continue;	/* index on wrong heap relation */
+ 			}
+ 		}
+ 
+ 		/*
+ 		 * If we made it here, we have work to do.	We make sure the heap
+ 		 * relation lock exists, then we walk the list of predicate locks for
+ 		 * the old target we found, moving all locks to the heap relation lock
+ 		 * -- unless they already hold that.
+ 		 */
+ 
+ 		/*
+ 		 * First make sure we have the heap relation target.  We only need to
+ 		 * do this once.
+ 		 */
+ 		if (heaptarget == NULL)
+ 		{
+ 			heaptarget = hash_search_with_hash_value(PredicateLockTargetHash,
+ 													 &heaptargettag,
+ 													 heaptargettaghash,
+ 													 HASH_ENTER, &found);
+ 			Assert(heaptarget != NULL);
+ 			if (!found)
+ 				SHMQueueInit(&heaptarget->predicateLocks);
+ 			newpredlocktag.myTarget = heaptarget;
+ 		}
+ 
+ 		/*
+ 		 * Loop through moving locks from this target to the relation target.
+ 		 */
+ 		oldpredlock = (PREDICATELOCK *)
+ 			SHMQueueNext(&(oldtarget->predicateLocks),
+ 						 &(oldtarget->predicateLocks),
+ 						 offsetof(PREDICATELOCK, targetLink));
+ 		while (oldpredlock)
+ 		{
+ 			PREDICATELOCK *nextpredlock;
+ 			PREDICATELOCK *newpredlock;
+ 			SerCommitSeqNo oldCommitSeqNo = oldpredlock->commitSeqNo;
+ 
+ 			nextpredlock = (PREDICATELOCK *)
+ 				SHMQueueNext(&(oldtarget->predicateLocks),
+ 							 &(oldpredlock->targetLink),
+ 							 offsetof(PREDICATELOCK, targetLink));
+ 			newpredlocktag.myXact = oldpredlock->tag.myXact;
+ 
+ 			SHMQueueDelete(&(oldpredlock->xactLink));
+ 			/* No need for retail delete from oldtarget list. */
+ 			hash_search(PredicateLockHash,
+ 						&oldpredlock->tag,
+ 						HASH_REMOVE, &found);
+ 			Assert(found);
+ 
+ 			newpredlock = (PREDICATELOCK *)
+ 				hash_search_with_hash_value
+ 				(PredicateLockHash,
+ 				 &newpredlocktag,
+ 				 PredicateLockHashCodeFromTargetHashCode(&newpredlocktag,
+ 														 heaptargettaghash),
+ 				 HASH_ENTER_NULL, &found);
+ 			Assert(newpredlock != NULL);
+ 			if (!found)
+ 			{
+ 				SHMQueueInsertBefore(&(heaptarget->predicateLocks),
+ 									 &(newpredlock->targetLink));
+ 				SHMQueueInsertBefore(&(newpredlocktag.myXact->predicateLocks),
+ 									 &(newpredlock->xactLink));
+ 				newpredlock->commitSeqNo = oldCommitSeqNo;
+ 			}
+ 			else
+ 			{
+ 				if (newpredlock->commitSeqNo < oldCommitSeqNo)
+ 					newpredlock->commitSeqNo = oldCommitSeqNo;
+ 			}
+ 
+ 			Assert(newpredlock->commitSeqNo != 0);
+ 			Assert((newpredlock->commitSeqNo == InvalidSerCommitSeqNo)
+ 				   || (newpredlock->tag.myXact == OldCommittedSxact));
+ 
+ 			oldpredlock = nextpredlock;
+ 		}
+ 
+ 		hash_search(PredicateLockTargetHash, &oldtarget->tag, HASH_REMOVE, &found);
+ 		Assert(found);
+ 	}
+ 
+ 	/* Put the reserved entry back */
+ 	hash_search_with_hash_value(PredicateLockTargetHash,
+ 								&ReservedTargetTag,
+ 								reservedtargettaghash,
+ 								HASH_ENTER, &found);
+ 	Assert(!found);
+ 
+ 	/* Release locks in reverse order */
+ 	LWLockRelease(SerializableXactHashLock);
+ 	for (i = NUM_PREDICATELOCK_PARTITIONS - 1; i >= 0; i--)
+ 		LWLockRelease(FirstPredicateLockMgrLock + i);
+ 	LWLockRelease(SerializablePredicateLockListLock);
+ }
+ 
  
  /*
   *		PredicateLockPageSplit
***************
*** 2646,2655 **** PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno,
  	bool		success;
  
  	/*
! 	 * Bail out quickly if there are no serializable transactions
! 	 * running. As with PredicateLockTupleRowVersionLink, it's safe to
! 	 * check this without taking locks because the caller is holding
! 	 * the buffer page lock.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
--- 2858,2866 ----
  	bool		success;
  
  	/*
! 	 * Bail out quickly if there are no serializable transactions running. As
! 	 * with PredicateLockTupleRowVersionLink, it's safe to check this without
! 	 * taking locks because the caller is holding the buffer page lock.
  	 */
  	if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
  		return;
*** a/src/include/catalog/index.h
--- b/src/include/catalog/index.h
***************
*** 66,71 **** extern void index_drop(Oid indexId);
--- 66,73 ----
  
  extern IndexInfo *BuildIndexInfo(Relation index);
  
+ extern Oid IndexGetRelation(Oid indexId);
+ 
  extern void FormIndexDatum(IndexInfo *indexInfo,
  			   TupleTableSlot *slot,
  			   EState *estate,
*** a/src/include/storage/predicate.h
--- b/src/include/storage/predicate.h
***************
*** 50,55 **** extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
--- 50,56 ----
  extern void PredicateLockTupleRowVersionLink(const Relation relation, const HeapTuple oldTuple, const HeapTuple newTuple);
  extern void PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
  extern void PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno, const BlockNumber newblkno);
+ extern void TransferPredicateLocksToHeapRelation(const Relation relation);
  extern void ReleasePredicateLocks(const bool isCommit);
  
  /* conflict detection (may also trigger rollback) */