xactlist.mbox

application/octet-stream

Filename: xactlist.mbox
Type: application/octet-stream
Part: 0
Message: Re: Do we need a ShmList implementation?
From e29927c7966adba2443fdc4f64da9d282f95a05b Mon Sep 17 00:00:00 2001
From: Kevin Grittner <Kevin.Grittner@wicourts.gov>
Date: Sat, 18 Sep 2010 11:08:40 -0500
Subject: [PATCH 1460/1461] Rework techniques for getting to top level transaction when using
 an XID from MVCC data, based on concerns form Heikki and Tom. Use
 SubTransGetTopmostTransaction, checking both before and after for
 a TranactionId before TransactionXmin, which would indicate a
 transaction too early to overlap, and therefore not able to
 generate a conflict with the current transaction.

---
 src/backend/access/transam/varsup.c      |    4 +
 src/backend/storage/lmgr/predicate.c     |  146 ++++++++++++------------------
 src/include/storage/predicate.h          |    1 +
 src/include/storage/predicate_internal.h |   15 ++--
 4 files changed, 70 insertions(+), 96 deletions(-)

diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index efe11a9..8811e24 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
 #include "storage/pmsignal.h"
+#include "storage/predicate.h"
 #include "storage/proc.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
@@ -156,9 +157,12 @@ GetNewTransactionId(bool isSubXact)
 	 * holds 32K or more transactions, so we don't have to do this very often.
 	 *
 	 * Extend pg_subtrans too.
+	 * If it's top level, the predicate locking system also needs to know.
 	 */
 	ExtendCLOG(xid);
 	ExtendSUBTRANS(xid);
+	if (!isSubXact)
+		RegisterPredicateLockingXid(xid);
 
 	/*
 	 * Now advance the nextXid counter.  This must not happen until after we
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 76e1277..974b95c 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -116,6 +116,7 @@
  *
  * predicate lock maintenance
  *		RegisterSerializableTransaction(Snapshot snapshot)
+ *		RegisterPredicateLockingXid(void)
  *		PredicateLockRelation(Relation relation)
  *		PredicateLockPage(Relation relation, BlockNumber blkno)
  *		PredicateLockTuple(Relation relation, HeapTuple tuple)
@@ -137,6 +138,7 @@
 
 #include "postgres.h"
 
+#include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
@@ -293,7 +295,6 @@ static bool GetParentPredicateLockTag(const PREDICATELOCKTARGETTAG *tag,
 						  PREDICATELOCKTARGETTAG *parent);
 static void DecrementParentLocks(const PREDICATELOCKTARGETTAG *targettag);
 static void PredicateLockAcquire(const PREDICATELOCKTARGETTAG *tag);
-static void EnsureMySerializableXidExists(void);
 static void ClearOldPredicateLocks(void);
 static bool XidIsConcurrent(TransactionId xid);
 static void FlagRWConflict(SERIALIZABLEXACT *reader, SERIALIZABLEXACT *writer);
@@ -612,7 +613,6 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 	sxact->finishedBefore = InvalidTransactionId;
 	sxact->xmin = snapshot->xmin;
 	SHMQueueInit(&(sxact->predicateLocks));
-	SHMQueueInit(&(sxact->xids));
 	SHMQueueElemInit(&(sxact->finishedLink));
 	sxact->rolledBack = false;
 	LWLockRelease(SerializableXactHashLock);
@@ -632,55 +632,50 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 }
 
 /*
- * Make sure we have a SERIALIZABLEXACT reference in MySerializableXact.
- * It should be current for this process and be contained in
- * SerializableXidHash.
+ * Register the top level XID in SerializableXidHash.
+ * Also store it for easy reference in MySerializableXact.
  */
-static void
-EnsureMySerializableXidExists(void)
+void
+RegisterPredicateLockingXid(const TransactionId xid)
 {
-	TransactionId xid;
-
-	Assert(MySerializableXact != InvalidSerializableXact);
-
-	MySerializableXact->topXid = GetTopTransactionIdIfAny();
+	SERIALIZABLEXIDTAG sxidtag;
+	SERIALIZABLEXID *sxid;
+	bool		found;
 
 	/*
-	 * If this isn't the xid we've most recently seen for this vxid, make sure
-	 * it's in the hash table.
+	 * If we're not tracking predicate lock data for this transaction,
+	 * we should ignore the request and return quickly.
 	 */
-	xid = GetCurrentTransactionIdIfAny();
-	if (MyXid != xid)
-	{
-		SERIALIZABLEXIDTAG sxidtag;
-		SERIALIZABLEXID *sxid;
-		bool		found;
+	if (MySerializableXact == InvalidSerializableXact)
+		return;
 
-		Assert(TransactionIdIsValid(xid));
+	/* This should only be done once per transaction. */
+	Assert(MySerializableXact->topXid == InvalidTransactionId);
 
-		sxidtag.xid = xid;
-		LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
-		sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
-											   &sxidtag,
-											   HASH_ENTER, &found);
-		if (!sxid)
-			ereport(ERROR,
-					(errcode(ERRCODE_OUT_OF_MEMORY),
-					 errmsg("out of shared memory"),
-					 errhint("You might need to increase max_predicate_locks_per_transaction.")));
+	/* We should have a valid XID and be at the top level. */
+	Assert(TransactionIdIsValid(xid));
 
-		/* Initialize the structure. */
-		if (!found)
-		{
-			sxid->myXact = (SERIALIZABLEXACT *) MySerializableXact;
-			SHMQueueInsertBefore(&(((SERIALIZABLEXACT *) MySerializableXact)->xids),
-								 &(sxid->xactLink));
-		}
-		LWLockRelease(SerializableXactHashLock);
-		MyXid = xid;
-	}
+	MySerializableXact->topXid = xid;
+
+	sxidtag.xid = xid;
+	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+	sxid = (SERIALIZABLEXID *) hash_search(SerializableXidHash,
+										   &sxidtag,
+										   HASH_ENTER, &found);
+	if (!sxid)
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				  errmsg("out of shared memory"),
+				  errhint("You might need to increase max_predicate_locks_per_transaction.")));
+
+	Assert(!found);
+
+	/* Initialize the structure. */
+	sxid->myXact = (SERIALIZABLEXACT *) MySerializableXact;
+	LWLockRelease(SerializableXactHashLock);
 }
 
+
 /*
  * Check whether there are any predicate locks held by any transaction
  * for the page at the given block number.
@@ -1053,8 +1048,6 @@ PredicateLockAcquire(const PREDICATELOCKTARGETTAG *targettag)
 	PREDICATELOCK *lock;
 	LOCALPREDICATELOCK *locallock;
 
-	EnsureMySerializableXidExists();
-
 	/* Do we have the lock already, or a covering lock? */
 	if (PredicateLockExists(targettag))
 		return;
@@ -1201,28 +1194,22 @@ PredicateLockTuple(const Relation relation, const HeapTuple tuple)
 		return;
 
 	/*
-	 * If it's a heap tuple, return if this xact wrote it.  It might be useful
-	 * to pass in the xmin from the tuple as another parameter.
+	 * If it's a heap tuple, return if this xact wrote it.
 	 */
 	if (relation->rd_index == NULL)
 	{
-		SERIALIZABLEXIDTAG sxidtag;
-		SERIALIZABLEXID *sxid;
-
-		sxidtag.xid = HeapTupleHeaderGetXmin(tuple->t_data);
-		LWLockAcquire(SerializableXactHashLock, LW_SHARED);
-		sxid = (SERIALIZABLEXID *)
-			hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
-		if (sxid)
+		TransactionId xid;
+
+		xid = HeapTupleHeaderGetXmin(tuple->t_data);
+		if (TransactionIdFollowsOrEquals(xid, TransactionXmin))
 		{
-			if (sxid->myXact == MySerializableXact)
+			xid = SubTransGetTopmostTransaction(xid);
+			if (xid == GetTopTransactionIdIfAny())
 			{
 				/* We wrote it; we already have a write lock. */
-				LWLockRelease(SerializableXactHashLock);
 				return;
 			}
 		}
-		LWLockRelease(SerializableXactHashLock);
 	}
 
 	tid = &(tuple->t_self);
@@ -1731,7 +1718,7 @@ static void
 ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact)
 {
 	PREDICATELOCK *predlock;
-	SERIALIZABLEXID *sxid;
+	SERIALIZABLEXIDTAG sxidtag;
 
 	Assert(sxact != NULL);
 	Assert(sxact->rolledBack || SxactIsCommitted(sxact));
@@ -1783,31 +1770,13 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact)
 	}
 	LWLockRelease(SerializablePredicateLockListLock);
 
-	/* Get rid of the xids and the record of the transaction itself. */
-	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
-	sxid = (SERIALIZABLEXID *)
-		SHMQueueNext(&(sxact->xids),
-					 &(sxact->xids),
-					 offsetof(SERIALIZABLEXID, xactLink));
-	while (sxid)
-	{
-		SERIALIZABLEXID *nextsxid;
-		SERIALIZABLEXIDTAG tag;
-
-		nextsxid = (SERIALIZABLEXID *)
-			SHMQueueNext(&(sxact->xids),
-						 &(sxid->xactLink),
-						 offsetof(SERIALIZABLEXID, xactLink));
-		tag = sxid->tag;
-		hash_search(SerializableXidHash, &tag, HASH_REMOVE, NULL);
-
-		/*
-		 * No need to do retail removal from transaction object; it's going
-		 * away.
-		 */
-		sxid = nextsxid;
-	}
 	SHMQueueDelete(&(sxact->finishedLink));
+
+	/* Get rid of the xid and the record of the transaction itself. */
+	sxidtag.xid = sxact->topXid;
+	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+	if (sxidtag.xid != InvalidTransactionId)
+		hash_search(SerializableXidHash, &sxidtag, HASH_REMOVE, NULL);
 	hash_search(SerializableXactHash, &(sxact->tag), HASH_REMOVE, NULL);
 	LWLockRelease(SerializableXactHashLock);
 }
@@ -1903,6 +1872,13 @@ CheckForSerializableConflictOut(const bool valid, const Relation relation,
 		xid = HeapTupleHeaderGetXmin(tuple->t_data);
 	}
 
+	/* Bail out if xid is too early to be a conflict. */
+	if (TransactionIdPrecedes(xid, TransactionXmin))
+		return;
+	xid = SubTransGetTopmostTransaction(xid);
+	if (TransactionIdPrecedes(xid, TransactionXmin))
+		return;
+
 	/*
 	 * It's OK to look for conflicts with a share lock, and record them with
 	 * an exclusive lock when found; we just have to release the shared lock
@@ -1949,12 +1925,6 @@ CheckForSerializableConflictOut(const bool valid, const Relation relation,
 	sxacttag = sxact->tag;
 	LWLockRelease(SerializableXactHashLock);
 
-	/*
-	 * Make sure we have somewhere to record a conflict against this
-	 * transaction.
-	 */
-	EnsureMySerializableXidExists();
-
 	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 	sxact = (SERIALIZABLEXACT *)
 		hash_search(SerializableXactHash, &sxacttag, HASH_FIND, NULL);
@@ -2184,8 +2154,6 @@ CheckForSerializableConflictIn(const Relation relation, const HeapTuple tuple,
 	if (SkipSerialization(relation))
 		return;
 
-	EnsureMySerializableXidExists();
-
 	/*
 	 * It is important that we check for locks from the finest granularity to
 	 * the coarsest granularity, so that granularity promotion doesn't cause
diff --git a/src/include/storage/predicate.h b/src/include/storage/predicate.h
index 7dcc2af..2cc20ab 100644
--- a/src/include/storage/predicate.h
+++ b/src/include/storage/predicate.h
@@ -37,6 +37,7 @@ extern bool PageIsPredicateLocked(const Relation relation, const BlockNumber blk
 
 /* predicate lock maintenance */
 extern void RegisterSerializableTransaction(const Snapshot snapshot);
+extern void RegisterPredicateLockingXid(const TransactionId xid);
 extern void PredicateLockRelation(const Relation relation);
 extern void PredicateLockPage(const Relation relation, const BlockNumber blkno);
 extern void PredicateLockTuple(const Relation relation, const HeapTuple tuple);
diff --git a/src/include/storage/predicate_internal.h b/src/include/storage/predicate_internal.h
index 7cdb5af..d6a2b8c 100644
--- a/src/include/storage/predicate_internal.h
+++ b/src/include/storage/predicate_internal.h
@@ -74,7 +74,6 @@ typedef struct SERIALIZABLEXACT
 										 * serializable xids are before this. */
 	TransactionId xmin;			/* the transaction's snapshot xmin */
 	SHM_QUEUE	predicateLocks; /* list of associated PREDICATELOCK objects */
-	SHM_QUEUE	xids;			/* list of associated SERIALIZABLEXID objects */
 	SHM_QUEUE	finishedLink;	/* list link in
 								 * FinishedSerializableTransactions */
 	bool		rolledBack;		/* ignore conflicts when true; allows deferred
@@ -93,8 +92,8 @@ typedef struct SERIALIZABLEXIDTAG
 
 /*
  * The SERIALIZABLEXID struct provides a link from a TransactionId for a
- * serializable transaction or subtransaction to the related SERIALIZABLEXACT
- * record.
+ * serializable transaction to the related SERIALIZABLEXACT record, even if
+ * the transaction has completed and its connection has been closed.
  * 
  * A hash table of these objects is maintained in shared memory to provide a
  * quick way to find the top level transaction information for a serializable
@@ -104,8 +103,12 @@ typedef struct SERIALIZABLEXIDTAG
  * allows a fast link from MVCC transaction IDs to the related serializable
  * transaction hash table entry.
  *
- * These are created as the transaction and its subtransactions write data,
- * and are kept until the cleanup of the top level transaction.
+ * These are created as new top level transaction IDs are first assigned to
+ * transactions which are participating in predicate locking.  They are
+ * removed with their related serializable transaction objects.
+ *
+ * The SubTransGetTopmostTransaction method is used where necessary to get
+ * from an XID which might be from a subtransaction to the top level XID.
  */
 typedef struct SERIALIZABLEXID
 {
@@ -114,8 +117,6 @@ typedef struct SERIALIZABLEXID
 
 	/* data */
 	SERIALIZABLEXACT *myXact;	/* pointer to the top level transaction data */
-	SHM_QUEUE	xactLink;		/* list link in SERIALIZABLEXACT's list of
-								 * xids */
 } SERIALIZABLEXID;
 
 
-- 
1.6.3.3


From b8eca245ab63725d0fbfc3b5969f4a17fc765f2c Mon Sep 17 00:00:00 2001
From: Kevin Grittner <Kevin.Grittner@wicourts.gov>
Date: Tue, 21 Sep 2010 10:11:26 -0500
Subject: [PATCH 1461/1461] Replace the hash table for SERIALIZABLEXACT objects with a list.
 The hash table was really only being used for memory allocation
 purposes; it was the SHM_QUEUE objects forming lists which really
 mattered.  That seemed inefficient and overly hacky, so a simple
 shared memory list implementation using SHM_QUEUE was created.
 The use of SHM_QUEUE is hidden, and all access is through four
 small functions, but no effort was made to really turn the list
 into a proper "black box" or generalize it.

---
 src/backend/storage/lmgr/predicate.c     |  186 +++++++++++++++++++++++-------
 src/include/storage/predicate_internal.h |   32 +++++
 2 files changed, 177 insertions(+), 41 deletions(-)

diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 974b95c..44c86e0 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -257,10 +257,21 @@ TransactionId SerializableGlobalXmin = InvalidTransactionId;
 int			SerializableGlobalXminCount = 0;
 
 /*
+ * This provides a list of objects in order to track transactions
+ * participating in predicate locking.  Entries in the list are fixed size,
+ * and reside in shared memory.  The memory address of an entry must remain
+ * fixed during its lifetime.  The list will be protected from concurrent
+ * update externally; no provision is made in this code to manage that.  The
+ * number of entries in the list, and the size allowed for each entry is
+ * fixed upon creation.
+ */
+static PredTranList PredLockTranList;
+
+
+/*
  * The predicate locking hash tables are in shared memory.
  * Each backend keeps pointers to them.
  */
-static HTAB *SerializableXactHash;
 static HTAB *SerializableXidHash;
 static HTAB *PredicateLockTargetHash;
 static HTAB *PredicateLockHash;
@@ -275,16 +286,19 @@ static HTAB *LocalPredicateLockHash = NULL;
 /*
  * Keep a pointer to the currently-running serializable transaction (if any)
  * for quick reference.
+ * TODO SSI: Remove volatile qualifier and the then-unnecessary casts?
  */
 static volatile SERIALIZABLEXACT *MySerializableXact = InvalidSerializableXact;
 
-/* TODO SSI: Remove volatile qualifier and the then-unnecessary casts? */
-
-/* The most recently used xid within this transaction, for optimizations. */
-static TransactionId MyXid = InvalidTransactionId;
 
 
 /* local functions */
+
+static SERIALIZABLEXACT *CreatePredTran(void);
+static void ReleasePredTran(SERIALIZABLEXACT *sxact);
+static SERIALIZABLEXACT *FirstPredTran(void);
+static SERIALIZABLEXACT *NextPredTran(SERIALIZABLEXACT *sxact);
+
 static uint32 predicatelock_hash(const void *key, Size keysize);
 static void ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact);
 static bool PredicateLockExists(const PREDICATELOCKTARGETTAG *newtargettag);
@@ -302,6 +316,78 @@ static void CheckTargetForConflictsIn(PREDICATELOCKTARGETTAG *targettag);
 static void OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
 										const SERIALIZABLEXACT *writer);
 
+
+/*
+ * These functions are a simple implementation of a list for this specific
+ * type of struct.  If there is ever a generalized shared memory list, we
+ * should probably switch to that.
+ */
+static SERIALIZABLEXACT *
+CreatePredTran(void)
+{
+	PredTranListElement ptle;
+
+	ptle = (PredTranListElement)
+		SHMQueueNext(&PredLockTranList->availableList,
+					 &PredLockTranList->availableList,
+					 offsetof(PredTranListElementData, link));
+	SHMQueueDelete(&ptle->link);
+	SHMQueueInsertBefore(&PredLockTranList->activeList, &ptle->link);
+	return &ptle->sxact;
+}
+
+static void
+ReleasePredTran(SERIALIZABLEXACT *sxact)
+{
+	PredTranListElement ptle;
+
+	Assert(ShmemAddrIsValid(sxact));
+
+	ptle = (PredTranListElement)
+		(((char *) sxact)
+			- offsetof(PredTranListElementData, sxact)
+			+ offsetof(PredTranListElementData, link));
+	SHMQueueDelete(&ptle->link);
+	SHMQueueInsertBefore(&PredLockTranList->availableList, &ptle->link);
+}
+
+static SERIALIZABLEXACT *
+FirstPredTran(void)
+{
+	PredTranListElement ptle;
+
+	ptle = (PredTranListElement)
+			SHMQueueNext(&PredLockTranList->activeList,
+						 &PredLockTranList->activeList,
+						 offsetof(PredTranListElementData, link));
+	if (!ptle)
+		return NULL;
+
+	return &ptle->sxact;
+}
+
+static SERIALIZABLEXACT *
+NextPredTran(SERIALIZABLEXACT *sxact)
+{
+	PredTranListElement ptle;
+
+	Assert(ShmemAddrIsValid(sxact));
+
+	ptle = (PredTranListElement)
+		(((char *) sxact)
+			- offsetof(PredTranListElementData, sxact)
+			+ offsetof(PredTranListElementData, link));
+	ptle = (PredTranListElement)
+			SHMQueueNext(&PredLockTranList->activeList,
+						 &ptle->link,
+						 offsetof(PredTranListElementData, link));
+	if (!ptle)
+		return NULL;
+
+	return &ptle->sxact;
+}
+
+
 /*
  * InitPredicateLocks -- Initialize the predicate locking data structures.
  *
@@ -319,6 +405,7 @@ InitPredicateLocks(void)
 	int			hash_flags;
 	long		init_table_size,
 				max_table_size;
+	Size		requestSize;
 	bool		found;
 
 	/*
@@ -375,25 +462,46 @@ InitPredicateLocks(void)
 	init_table_size = max_table_size / 2;
 
 	/*
-	 * Allocate hash table for SERIALIZABLEXACT structs.  This stores per-vxid
-	 * information for serializable transactions which have accessed data.
+	 * Allocate a list to hold information on transaction participating in
+	 * predicate locking.
+	 *
+	 * Assume an average of 10 predicate locking transactions per backend.
+	 * That may seem high, but each transaction must be kept until every
+	 * overlapping predicate locking transaction has completed, so we have
+	 * to tolerate the occassional long-running transaction.
 	 */
-	MemSet(&info, 0, sizeof(info));
-	info.keysize = sizeof(SERIALIZABLEXACTTAG);
-	info.entrysize = sizeof(SERIALIZABLEXACT);
-	info.hash = tag_hash;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION);
-
-	SerializableXactHash = ShmemInitHash("SERIALIZABLEXACT hash",
-										 init_table_size,
-										 max_table_size,
-										 &info,
-										 hash_flags);
-
-	/* Assume an average of 10 serializable xids per backend. */
 	max_table_size *= 10;
 	init_table_size *= 10;
 
+	PredLockTranList = ShmemInitStruct("PredTranList",
+									   PredTranListDataSize,
+									   &found);
+	if (!found)
+	{
+		int i;
+
+		SHMQueueInit(&PredLockTranList->availableList);  // available elements
+		SHMQueueInit(&PredLockTranList->activeList);  // active elements
+		requestSize = mul_size((Size) max_table_size,
+							   PredTranListElementDataSize);
+		PredLockTranList->element = ShmemAlloc(requestSize);
+		if (PredLockTranList->element == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					  errmsg("not enough shared memory for elements of data structure"
+							" \"%s\" (%lu bytes requested)",
+							"PredTranList", (unsigned long) requestSize)));
+		PredLockTranList->entryLimit = max_table_size;  // get from GUC?
+		PredLockTranList->entryCount = 0;  // starts empty
+		/* Add all elements to available list, clean. */
+		memset(PredLockTranList->element, 0, requestSize);
+		for (i = 0; i < max_table_size; i++)
+		{
+			SHMQueueInsertBefore(&(PredLockTranList->availableList),
+								 &(PredLockTranList->element[i].link));
+		}
+	}
+
 	/*
 	 * Allocate hash table for SERIALIZABLEXID structs.  This stores per-xid
 	 * information for serializable transactions which have accessed data.
@@ -447,13 +555,14 @@ PredicateLockShmemSize(void)
 	 */
 	size = add_size(size, size / 10);
 
-	/* serializable transaction table */
+	/* transaction list */
 	max_table_size = MaxBackends;
-	size = add_size(size, hash_estimate_size(max_table_size,
-											 sizeof(SERIALIZABLEXACT)));
-
-	/* serializable subtransaction table */
 	max_table_size *= 10;
+	size = add_size(size, PredTranListDataSize);
+	size = add_size(size, mul_size((Size) max_table_size,
+								   PredTranListElementDataSize));
+
+	/* transaction xid table */
 	size = add_size(size, hash_estimate_size(max_table_size,
 											 sizeof(SERIALIZABLEXID)));
 
@@ -561,7 +670,7 @@ GetPredicateLockStatusData(void)
 /*
  * Make sure we have a SERIALIZABLEXACT reference in MySerializableXact.
  * It should be current for this process and be contained in
- * SerializableXactHash.
+ * PredLockTranList.
  */
 void
 RegisterSerializableTransaction(const Snapshot snapshot)
@@ -569,7 +678,6 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 	PGPROC	   *proc;
 	SERIALIZABLEXACTTAG sxacttag;
 	SERIALIZABLEXACT *sxact;
-	bool		found;
 	HASHCTL		hash_ctl;
 
 	/* We only do this for serializable transactions.  Once. */
@@ -596,10 +704,8 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 	{
 		Assert(TransactionIdFollows(snapshot->xmin, SerializableGlobalXmin));
 	}
-	sxact = (SERIALIZABLEXACT *) hash_search(SerializableXactHash,
-											 &sxacttag,
-											 HASH_ENTER, &found);
-	Assert(!found);
+	sxact = CreatePredTran();
+	/* TODO SSI: If null, cancel oldest tran and try again? */
 	if (!sxact)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
@@ -607,6 +713,7 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 				 errhint("You might need to increase max_predicate_locks_per_transaction.")));
 
 	/* Initialize the structure. */
+	sxact->tag = sxacttag;
 	sxact->outConflict = InvalidSerializableXact;
 	sxact->inConflict = InvalidSerializableXact;
 	sxact->topXid = GetTopTransactionIdIfAny();
@@ -1542,13 +1649,12 @@ PredicateLockPageCombine(const Relation relation, const BlockNumber oldblkno,
 static void
 SetNewSerializableGlobalXmin(void)
 {
-	HASH_SEQ_STATUS seqstat;
 	SERIALIZABLEXACT *sxact;
 
 	SerializableGlobalXmin = InvalidTransactionId;
 	SerializableGlobalXminCount = 0;
-	hash_seq_init(&seqstat, SerializableXactHash);
-	while ((sxact = (SERIALIZABLEXACT *) hash_seq_search(&seqstat)))
+
+	for (sxact = FirstPredTran(); sxact != NULL; sxact = NextPredTran(sxact))
 	{
 		if (!SxactIsOnFinishedList(sxact))
 		{
@@ -1662,7 +1768,6 @@ ReleasePredicateLocks(const bool isCommit)
 		ClearOldPredicateLocks();
 
 	MySerializableXact = InvalidSerializableXact;
-	MyXid = InvalidTransactionId;
 
 	/* Delete per-transaction lock table */
 	hash_destroy(LocalPredicateLockHash);
@@ -1777,7 +1882,7 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact)
 	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
 	if (sxidtag.xid != InvalidTransactionId)
 		hash_search(SerializableXidHash, &sxidtag, HASH_REMOVE, NULL);
-	hash_search(SerializableXactHash, &(sxact->tag), HASH_REMOVE, NULL);
+	ReleasePredTran(sxact);
 	LWLockRelease(SerializableXactHashLock);
 }
 
@@ -1838,7 +1943,6 @@ CheckForSerializableConflictOut(const bool valid, const Relation relation,
 	TransactionId xid;
 	SERIALIZABLEXIDTAG sxidtag;
 	SERIALIZABLEXID *sxid;
-	SERIALIZABLEXACTTAG sxacttag;
 	SERIALIZABLEXACT *sxact;
 
 	if (SkipSerialization(relation))
@@ -1922,18 +2026,18 @@ CheckForSerializableConflictOut(const bool valid, const Relation relation,
 		return;
 	}
 
-	sxacttag = sxact->tag;
 	LWLockRelease(SerializableXactHashLock);
 
 	LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
-	sxact = (SERIALIZABLEXACT *)
-		hash_search(SerializableXactHash, &sxacttag, HASH_FIND, NULL);
-	if (!sxact)
+	sxid = (SERIALIZABLEXID *)
+		hash_search(SerializableXidHash, &sxidtag, HASH_FIND, NULL);
+	if (!sxid)
 	{
 		/* It must have been cleaned up, which means it wasn't useful. */
 		LWLockRelease(SerializableXactHashLock);
 		return;
 	}
+	Assert(sxid->myXact == sxact);
 	xid = sxact->topXid;
 	if (!XidIsConcurrent(xid))
 	{
diff --git a/src/include/storage/predicate_internal.h b/src/include/storage/predicate_internal.h
index d6a2b8c..222fc74 100644
--- a/src/include/storage/predicate_internal.h
+++ b/src/include/storage/predicate_internal.h
@@ -80,6 +80,38 @@ typedef struct SERIALIZABLEXACT
 								 * cleanup */
 } SERIALIZABLEXACT;
 
+/*
+ * The following types are used to provide an ad hoc list for holding
+ * SERIALIZABLEXACT objects.  An HTAB is overkill, since there is no need to
+ * access these by key -- there are direct pointers to these objects where
+ * needed.  If a shared memory list is created, these types can probably be
+ * eliminated in favor of using the general solution.
+ */
+typedef struct PredTranListElementData
+{
+	SHM_QUEUE	link;
+	SERIALIZABLEXACT	sxact;
+} PredTranListElementData;
+
+typedef struct PredTranListElementData * PredTranListElement;
+
+#define PredTranListElementDataSize \
+		((Size)MAXALIGN(sizeof(PredTranListElementData)))
+
+typedef struct PredTranListData
+{
+	SHM_QUEUE	availableList;
+	SHM_QUEUE	activeList;
+	PredTranListElement	element;
+	int	entryLimit;
+	int entryCount;
+} PredTranListData;
+
+typedef struct PredTranListData *PredTranList;
+
+#define PredTranListDataSize \
+		((Size)MAXALIGN(sizeof(PredTranListData)))
+
 
 /*
  * The SERIALIZABLEXIDTAG struct identifies an xid assigned to a serializable
-- 
1.6.3.3