Work in progress for change from conflict pointers to conflict lists.
authorKevin Grittner <[email protected]>
Sun, 14 Nov 2010 22:27:53 +0000 (16:27 -0600)
committerKevin Grittner <[email protected]>
Sun, 14 Nov 2010 22:27:53 +0000 (16:27 -0600)
This also changes the rolledBack flag to an int with multiple flags.
With this commit it compiles clean and passes "make check" tests,
but fails half of the "make dcheck" tests.  Little effort has been
made so far to handle the commit order and READ ONLY special cases.

src/backend/storage/lmgr/predicate.c
src/include/storage/predicate_internals.h

index 85326e512fbb82c3d971beac849865f03978feea..fd162fab59f1d64ae71228e68a67f97e68565b64 100644 (file)
@@ -286,6 +286,11 @@ int            SerializableGlobalXminCount = 0;
  */
 static PredTranList PredLockTranList;
 
+/*
+ * This provides a pool of RWConflict data elements to use in conflict lists
+ * between transactions.
+ */
+static RWConflictPoolHeader RWConflictPool;
 
 /*
  * The predicate locking hash tables are in shared memory.
@@ -410,6 +415,76 @@ NextPredTran(SERIALIZABLEXACT *sxact)
 }
 
 
+/*
+ * These functions manage primitive access to the RWConflict pool and lists.
+ */
+static bool
+RWConflictExists(const SERIALIZABLEXACT *reader, const SERIALIZABLEXACT *writer)
+{
+   RWConflict conflict;
+
+   Assert(reader != writer);
+
+   /* Check the ends of the purported conflict first. */
+   if ((reader->flags & SXACT_FLAG_ROLLED_BACK)
+       || (writer->flags & SXACT_FLAG_ROLLED_BACK)
+       || SHMQueueIsDetached((SHM_QUEUE *) &reader->outConflicts)
+       || SHMQueueIsDetached((SHM_QUEUE *) &writer->inConflicts))
+       return false;
+
+   /* A conflict is possible; walk the list to find out. */
+   conflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &reader->outConflicts,
+                    (SHM_QUEUE *) &reader->outConflicts,
+                    offsetof(RWConflictData, outLink));
+   while (conflict)
+   {
+       if (conflict->sxactIn == writer)
+           return true;
+       conflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &reader->outConflicts,
+                        &conflict->outLink,
+                        offsetof(RWConflictData, outLink));
+   }
+
+   /* No conflict found. */
+   return false;
+}
+
+static void
+SetRWConflict(SERIALIZABLEXACT *reader, SERIALIZABLEXACT *writer)
+{
+   RWConflict conflict;
+
+   Assert(reader != writer);
+   Assert(!RWConflictExists(reader, writer));
+
+   conflict = (RWConflict)
+       SHMQueueNext(&RWConflictPool->availableList,
+                    &RWConflictPool->availableList,
+                    offsetof(RWConflictData, outLink));
+   if (!conflict)
+       ereport(ERROR,
+               (errcode(ERRCODE_OUT_OF_MEMORY),
+         errmsg("not enough elements in RWConflictPool to record a rw-conflict")));
+
+   SHMQueueDelete(&conflict->outLink);
+
+   conflict->sxactOut = reader;
+   conflict->sxactIn = writer;
+   SHMQueueInsertBefore(&reader->outConflicts, &conflict->outLink);
+   SHMQueueInsertBefore(&writer->inConflicts, &conflict->inLink);
+}
+
+static void
+ReleaseRWConflict(RWConflict conflict)
+{
+   SHMQueueDelete(&conflict->inLink);
+   SHMQueueDelete(&conflict->outLink);
+   SHMQueueInsertBefore(&RWConflictPool->availableList, &conflict->outLink);
+}
+
+
 /*
  * InitPredicateLocks -- Initialize the predicate locking data structures.
  *
@@ -513,8 +588,6 @@ InitPredicateLocks(void)
             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++)
@@ -540,6 +613,42 @@ InitPredicateLocks(void)
                                        &info,
                                        hash_flags);
 
+   /*
+    * Allocate space for tracking rw-conflicts in lists attached to the
+    * transactions.
+    *
+    * TODO SSI: Assume an average of 5 conflicts per transaction.
+    * This is likely to need to be adjusted or configured by a GUC.
+    * Gotta start somewhere....
+    */
+   max_table_size *= 5;
+
+   RWConflictPool = ShmemInitStruct("RWConflictPool",
+                                    RWConflictPoolHeaderDataSize,
+                                    &found);
+   if (!found)
+   {
+       int         i;
+
+       SHMQueueInit(&RWConflictPool->availableList);
+       requestSize = mul_size((Size) max_table_size,
+                              PredTranListElementDataSize);
+       RWConflictPool->element = ShmemAlloc(requestSize);
+       if (RWConflictPool->element == NULL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_OUT_OF_MEMORY),
+            errmsg("not enough shared memory for elements of data structure"
+                   " \"%s\" (%lu bytes requested)",
+                   "RWConflictPool", (unsigned long) requestSize)));
+       /* Add all elements to available list, clean. */
+       memset(RWConflictPool->element, 0, requestSize);
+       for (i = 0; i < max_table_size; i++)
+       {
+           SHMQueueInsertBefore(&(RWConflictPool->availableList),
+                                &(RWConflictPool->element[i].outLink));
+       }
+   }
+
    /*
     * Create or attach to the header for the list of finished serializable
     * transactions.
@@ -736,14 +845,14 @@ RegisterSerializableTransaction(const Snapshot snapshot)
 
    /* Initialize the structure. */
    sxact->tag = sxacttag;
-   sxact->outConflict = InvalidSerializableXact;
-   sxact->inConflict = InvalidSerializableXact;
+   SHMQueueInit(&(sxact->outConflicts));
+   SHMQueueInit(&(sxact->inConflicts));
    sxact->topXid = GetTopTransactionIdIfAny();
    sxact->finishedBefore = InvalidTransactionId;
    sxact->xmin = snapshot->xmin;
    SHMQueueInit(&(sxact->predicateLocks));
    SHMQueueElemInit(&(sxact->finishedLink));
-   sxact->rolledBack = false;
+   sxact->flags = 0;
    LWLockRelease(SerializableXactHashLock);
 
    MySerializableXact = sxact;
@@ -1717,6 +1826,7 @@ void
 ReleasePredicateLocks(const bool isCommit)
 {
    bool        needToClear;
+   RWConflict  conflict, nextConflict;
 
    if (MySerializableXact == InvalidSerializableXact)
    {
@@ -1731,28 +1841,56 @@ ReleasePredicateLocks(const bool isCommit)
 
    /*
     * If it's not a commit it's a rollback, and we can clear our locks
-    * immediately.  TODO SSI: Clear the locks, but leave the sxact record.
+    * immediately.
     */
    if (!isCommit)
-       MySerializableXact->rolledBack = true;
+       MySerializableXact->flags |= SXACT_FLAG_ROLLED_BACK;
 
    /*
-    * Add this to the list of transactions to check for later cleanup. First
-    * turn pointers to already-terminated transactions to self-references.
+    * Release all outConflicts from committed transactions, but set
+    * SXACT_FLAG_CONFLICT_OUT if any exist.
+    * If we're rolling back clear them all.
     */
-   if (MySerializableXact->inConflict != InvalidSerializableXact)
+   conflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &MySerializableXact->outConflicts,
+                    (SHM_QUEUE *) &MySerializableXact->outConflicts,
+                    offsetof(RWConflictData, outLink));
+   if (conflict && !isCommit)
+       MySerializableXact->flags |= SXACT_FLAG_CONFLICT_OUT;
+   while (conflict)
    {
-       if (MySerializableXact->inConflict->rolledBack)
-           MySerializableXact->inConflict = InvalidSerializableXact;
-       else if (SxactIsCommitted(MySerializableXact->inConflict))
-           MySerializableXact->inConflict = (SERIALIZABLEXACT *) MySerializableXact;
+       nextConflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &MySerializableXact->outConflicts,
+                        &conflict->outLink,
+                        offsetof(RWConflictData, outLink));
+
+       if (!isCommit
+           || (conflict->sxactIn->flags & SXACT_FLAG_COMMITTED))
+           ReleaseRWConflict(conflict);
+
+       conflict = nextConflict;
    }
-   if (MySerializableXact->outConflict != InvalidSerializableXact)
+
+   /*
+    * Release all inConflicts from committed transactions.
+    * If we're rolling back, clear them all.
+    */
+   conflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    (SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    offsetof(RWConflictData, inLink));
+   while (conflict)
    {
-       if (MySerializableXact->outConflict->rolledBack)
-           MySerializableXact->outConflict = InvalidSerializableXact;
-       else if (SxactIsCommitted(MySerializableXact->outConflict))
-           MySerializableXact->outConflict = (SERIALIZABLEXACT *) MySerializableXact;
+       nextConflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                        &conflict->inLink,
+                        offsetof(RWConflictData, inLink));
+
+       if (!isCommit
+           || (conflict->sxactOut->flags & SXACT_FLAG_COMMITTED))
+           ReleaseRWConflict(conflict);
+
+       conflict = nextConflict;
    }
 
    /* Add this to the list of transactions to check for later cleanup. */
@@ -1846,9 +1984,10 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact)
 {
    PREDICATELOCK *predlock;
    SERIALIZABLEXIDTAG sxidtag;
+   RWConflict conflict, nextConflict;
 
    Assert(sxact != NULL);
-   Assert(sxact->rolledBack || SxactIsCommitted(sxact));
+   Assert((sxact->flags & SXACT_FLAG_ROLLED_BACK) || SxactIsCommitted(sxact));
    Assert(SxactIsOnFinishedList(sxact));
 
    LWLockAcquire(SerializablePredicateLockListLock, LW_SHARED);
@@ -1899,12 +2038,44 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact)
 
    SHMQueueDelete(&(sxact->finishedLink));
 
-   /* Get rid of the xid and the record of the transaction itself. */
    sxidtag.xid = sxact->topXid;
    LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+
+   /* Release all outConflicts. */
+   conflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &MySerializableXact->outConflicts,
+                    (SHM_QUEUE *) &MySerializableXact->outConflicts,
+                    offsetof(RWConflictData, outLink));
+   while (conflict)
+   {
+       nextConflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &MySerializableXact->outConflicts,
+                        &conflict->outLink,
+                        offsetof(RWConflictData, outLink));
+       ReleaseRWConflict(conflict);
+       conflict = nextConflict;
+   }
+
+   /* Release all inConflicts. */
+   conflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    (SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    offsetof(RWConflictData, inLink));
+   while (conflict)
+   {
+       nextConflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                        &conflict->inLink,
+                        offsetof(RWConflictData, inLink));
+       ReleaseRWConflict(conflict);
+       conflict = nextConflict;
+   }
+
+   /* Get rid of the xid and the record of the transaction itself. */
    if (sxidtag.xid != InvalidTransactionId)
        hash_search(SerializableXidHash, &sxidtag, HASH_REMOVE, NULL);
    ReleasePredTran(sxact);
+
    LWLockRelease(SerializableXactHashLock);
 }
 
@@ -2024,29 +2195,30 @@ CheckForSerializableConflictOut(const bool valid, const Relation relation,
        return;
    }
    sxact = sxid->myXact;
-   if (sxact == MySerializableXact || sxact->rolledBack)
+   if (sxact == MySerializableXact
+       || RWConflictExists((SERIALIZABLEXACT *) MySerializableXact, sxact))
    {
        /* We can't conflict with our own transaction or one rolled back. */
        LWLockRelease(SerializableXactHashLock);
        return;
    }
 
-   /*
-    * If this is a read-only transaction and the writing transaction has
-    * committed, and it doesn't have a rw-conflict out or has a conflict out
-    * to a transaction which overlaps this transaction, then no conflict.
-    */
-   if (XactReadOnly
-       && SxactIsCommitted(sxact)
-       && (!TransactionIdIsValid(sxact->outConflict)
-           || (sxact != sxact->outConflict
-               && (!SxactIsCommitted(sxact->outConflict)
-                   || XidIsConcurrent(sxact->outConflict->topXid)))))
-   {
-       /* Read-only transaction will appear to run first.  No conflict. */
-       LWLockRelease(SerializableXactHashLock);
-       return;
-   }
+//     /*
+//      * If this is a read-only transaction and the writing transaction has
+//      * committed, and it doesn't have a rw-conflict out or has a conflict out
+//      * to a transaction which overlaps this transaction, then no conflict.
+//      */
+//     if (XactReadOnly
+//         && SxactIsCommitted(sxact)
+//         && (!TransactionIdIsValid(sxact->outConflict)
+//             || (sxact != sxact->outConflict
+//                 && (!SxactIsCommitted(sxact->outConflict)
+//                     || XidIsConcurrent(sxact->outConflict->topXid)))))
+//     {
+//         /* Read-only transaction will appear to run first.  No conflict. */
+//         LWLockRelease(SerializableXactHashLock);
+//         return;
+//     }
 
    LWLockRelease(SerializableXactHashLock);
 
@@ -2232,12 +2404,11 @@ CheckTargetForConflictsIn(PREDICATELOCKTARGETTAG *targettag)
                }
            }
        }
-       else if (!(sxact->rolledBack)
+       else if (!(sxact->flags & SXACT_FLAG_ROLLED_BACK)
                 && (!SxactIsCommitted(sxact)
                     || TransactionIdPrecedes(GetTransactionSnapshot()->xmin,
                                              sxact->finishedBefore))
-                && sxact->outConflict != MySerializableXact
-                && MySerializableXact->inConflict != sxact)
+                && !RWConflictExists(sxact, (SERIALIZABLEXACT *) MySerializableXact))
        {
            LWLockRelease(SerializableXactHashLock);
            LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
@@ -2316,9 +2487,6 @@ CheckForSerializableConflictIn(const Relation relation, const HeapTuple tuple,
 
 /*
  * Flag a rw-dependency between two serializable transactions.
- * If a conflict field is invalid set it to the other transaction,
- * if it's already the other transaction leave it alone, otherwise
- * use self-reference (so we don't need to keep a list).
  *
  * The caller is responsible for ensuring that we have a LW lock on
  * the transaction hash table.
@@ -2332,16 +2500,7 @@ FlagRWConflict(SERIALIZABLEXACT *reader, SERIALIZABLEXACT *writer)
    OnConflict_CheckForSerializationFailure(reader, writer);
 
    /* Actually do the conflict flagging. */
-   if (writer->inConflict == InvalidSerializableXact
-       || writer->inConflict->rolledBack)
-       writer->inConflict = reader;
-   else if (writer->inConflict != reader)
-       writer->inConflict = writer;
-   if (reader->outConflict == InvalidSerializableXact
-       || reader->outConflict->rolledBack)
-       reader->outConflict = writer;
-   else if (reader->outConflict != writer)
-       reader->outConflict = reader;
+   SetRWConflict(reader, writer);
 }
 
 /*
@@ -2358,65 +2517,14 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
 
    failure = false;
 
-   if (writer->inConflict != reader
-       && writer->outConflict != InvalidSerializableXact
-       && !(writer->outConflict->rolledBack))
-   {
-       /* The writer is or is becoming a pivot. */
-       /* Self-reference prevents checking commit sequence. */
-       if (writer->outConflict == writer
+   if (!SHMQueueIsDetached((SHM_QUEUE *) &reader->inConflicts)
+       && (writer->flags & SXACT_FLAG_COMMITTED)
+       /* TODO SSI: check for READ ONLY special case */)
+       failure = true;
 
-       /*
-        * TODO SSI: Resolve this performance tweak issue.
-        *
-        * Back-and-forth reference is write skew; thus doomed; however,
-        * rolling back here increases chances that a retry will still fail.
-        * It may be better to let it happen at commit time.  Only performance
-        * testing can determine whether the next line should be used.
-        *
-        * Leaving it out would be *especially* valuable if the PreCommit
-        * checking could be changed to allow a commit in a situation where it
-        * is leaving another transaction in a state where a commit must fail
-        * -- when the doomed transaction eventually tries to commit, it would
-        * probably be at a time when an immediate retry is very likely to
-        * succeed.
-        */
-       /* || writer->outConflict == reader */
-           )
-           failure = true;
-       else if (SxactIsCommitted(writer->outConflict))
-       {
-           if (SxactCommittedBefore(writer->outConflict, writer)
-               && SxactCommittedBefore(writer->outConflict, reader))
-               /* The out side of the pivot committed first. */
-               failure = true;
-       }
-       else
-       {
-           if (writer->outConflict->inConflict == writer->outConflict)
-               /* Self-reference will prevent checking at commit. */
-               failure = true;
-       }
-   }
-
-   if (reader->outConflict != writer
-       && reader->inConflict != InvalidSerializableXact
-       && !(reader->inConflict->rolledBack))
-   {
-       /* The reader is or is becoming a pivot. */
-       if (SxactIsCommitted(writer))
-       {
-           if (SxactCommittedBefore(writer, reader)
-               && (reader->inConflict == reader
-                   || SxactCommittedBefore(writer, reader->inConflict)))
-               /* The out side committed first, as far as we can tell. */
-               failure = true;
-       }
-       else if (writer->inConflict != InvalidSerializableXact
-                && writer->inConflict != reader)
-           /* Self-reference will prevent checking at commit. */
-           failure = true;
-   }
+   if (!SHMQueueIsDetached((SHM_QUEUE *) &writer->outConflicts)
+       /* TODO SSI: check for commit in any of the out conflicts, and READ ONLY special case */)
+       failure = true;
 
    if (failure)
        ereport(ERROR,
@@ -2432,31 +2540,65 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
  *
  * We're checking for a dangerous structure as each conflict is recorded.
  * The only way we could have a problem at commit is if this is the "out"
- * side of a pivot, and neither the "in" side or the pivot itself has yet
+ * side of a pivot, and neither the "in" side nor the pivot has yet
  * committed.
  */
 void
 PreCommit_CheckForSerializationFailure(void)
 {
+   bool failure;
+   RWConflict nearConflict;
+
    if (MySerializableXact == InvalidSerializableXact)
        return;
 
    Assert(IsolationIsSerializable());
 
-   LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+   failure = false;
+
+   /* TODO SSI: check whether another transaction has cancelled us? */
 
    /*
-    * Checking at conflict detection should only allow self-reference in if
-    * this transaction is on the on the out side of a pivot, so
-    * self-reference is OK here.
+    * TODO SSI: SHARED here and EXCLUSIVE below to modify?  Would require
+    * new SerializableCommitLock for exclusive use around this method?
     */
-   if (MySerializableXact->inConflict != InvalidSerializableXact
-       && MySerializableXact->inConflict != MySerializableXact
-       && !(MySerializableXact->inConflict->rolledBack)
-    && MySerializableXact->inConflict->inConflict != InvalidSerializableXact
-       && !SxactIsCommitted(MySerializableXact->inConflict)
-       && !SxactIsCommitted(MySerializableXact->inConflict->inConflict))
+   LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
+
+   nearConflict = (RWConflict)
+       SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    (SHM_QUEUE *) &MySerializableXact->inConflicts,
+                    offsetof(RWConflictData, inLink));
+   while (nearConflict && !failure)
    {
+       RWConflict farConflict;
+
+       if (!(nearConflict->sxactIn->flags & SXACT_FLAG_COMMITTED))
+       {
+           farConflict = (RWConflict)
+               SHMQueueNext(&nearConflict->sxactIn->inConflicts,
+                            &nearConflict->sxactIn->inConflicts,
+                            offsetof(RWConflictData, inLink));
+           while (farConflict && !failure)
+           {
+               if (!(farConflict->sxactIn->flags & SXACT_FLAG_COMMITTED))
+                   failure = true;
+               else
+                   farConflict = (RWConflict)
+                       SHMQueueNext(&nearConflict->sxactIn->inConflicts,
+                                    &farConflict->inLink,
+                                    offsetof(RWConflictData, inLink));
+           }
+       }
+
+       nearConflict = (RWConflict)
+           SHMQueueNext((SHM_QUEUE *) &MySerializableXact->inConflicts,
+                        &nearConflict->inLink,
+                        offsetof(RWConflictData, inLink));
+   }
+
+   if (failure)
+   {
+       /* TODO SSI: cancel some *other* transaction(s) here, instead! */
        MySerializableXact->finishedBefore = ShmemVariableCache->nextXid;
        ereport(ERROR,
                (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
@@ -2465,5 +2607,7 @@ PreCommit_CheckForSerializationFailure(void)
    }
 
    MySerializableXact->finishedBefore = ShmemVariableCache->nextXid;
+   MySerializableXact->flags |= SXACT_FLAG_COMMITTED;
+
    LWLockRelease(SerializableXactHashLock);
 }
index 60795776bc41f2dde1fe983bc88f2b018703fc40..50033ed71cd32a4efa51f4e632b7c4879235f3d9 100644 (file)
@@ -41,13 +41,6 @@ typedef struct SERIALIZABLEXACTTAG
  *
  * Eligibility for cleanup of committed transactions is determined by
  * comparing the transaction's finishedBefore field to SerializableGlobalXmin.
- *
- * TODO SSI: Should inConflict and outConflict be lists?  That would allow us
- *          to reduce false positives, *and* would allow us to guarantee
- *          that an immediate retry of a transaction would never fail on the
- *          exact same conflicts.  The RAM doesn't look like it would be the
- *          limiting factor, but CPU time or LWLock contention might be --
- *          we should have baseline benchmarks before attempting this.
  */
 typedef struct SERIALIZABLEXACT
 {
@@ -55,18 +48,10 @@ typedef struct SERIALIZABLEXACT
    SERIALIZABLEXACTTAG tag;
 
    /* data */
-   struct SERIALIZABLEXACT *outConflict;       /* ptr to write transaction
-                                                * whose data we couldn't
-                                                * read. invalid means no
-                                                * conflict; self-reference
-                                                * means multiple or
-                                                * committed. */
-   struct SERIALIZABLEXACT *inConflict;        /* ptr to read transaction
-                                                * which couldn't see our
-                                                * write. invalid means no
-                                                * conflict; self-reference
-                                                * means multiple or
-                                                * committed. */
+   SHM_QUEUE   outConflicts;   /* list of write transactions whose data we
+                                * couldn't read. */
+   SHM_QUEUE   inConflicts;    /* list of read transactions which couldn't
+                                * see our write. */
    TransactionId topXid;       /* top level xid for the transaction, if one
                                 * exists; else invalid */
    TransactionId finishedBefore;       /* invalid means still running; else
@@ -76,10 +61,17 @@ typedef struct SERIALIZABLEXACT
    SHM_QUEUE   predicateLocks; /* list of associated PREDICATELOCK objects */
    SHM_QUEUE   finishedLink;   /* list link in
                                 * FinishedSerializableTransactions */
-   bool        rolledBack;     /* ignore conflicts when true; allows deferred
-                                * cleanup */
+   int         flags;
 } SERIALIZABLEXACT;
 
+/* TODO SSI: What's the best technique for dealing with these flags? */
+#define SXACT_FLAG_ROLLED_BACK 1
+#define SXACT_FLAG_COMMITTED   2
+#define SXACT_FLAG_CONFLICT_OUT    4
+#define SXACT_FLAG_PIVOT_OUT   8
+#define SXACT_FLAG_READ_ONLY   16
+
+
 /*
  * 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
@@ -103,8 +95,6 @@ typedef struct PredTranListData
    SHM_QUEUE   availableList;
    SHM_QUEUE   activeList;
    PredTranListElement element;
-   int         entryLimit;
-   int         entryCount;
 } PredTranListData;
 
 typedef struct PredTranListData *PredTranList;
@@ -113,6 +103,38 @@ typedef struct PredTranListData *PredTranList;
        ((Size)MAXALIGN(sizeof(PredTranListData)))
 
 
+/*
+ * The following types are used to provide lists of rw-conflicts between
+ * pairs of transactions.
+ *
+ * The outList field doubles for an "available" list when the structure
+ * is not in use.
+ */
+typedef struct RWConflictData
+{
+   SHM_QUEUE   outLink;
+   SHM_QUEUE   inLink;
+   SERIALIZABLEXACT    *sxactOut;
+   SERIALIZABLEXACT    *sxactIn;
+} RWConflictData;
+
+typedef struct RWConflictData *RWConflict;
+
+#define RWConflictDataSize \
+       ((Size)MAXALIGN(sizeof(RWConflictData)))
+
+typedef struct RWConflictPoolHeaderData
+{
+   SHM_QUEUE   availableList;
+   RWConflict  element;
+} RWConflictPoolHeaderData;
+
+typedef struct RWConflictPoolHeaderData *RWConflictPoolHeader;
+
+#define RWConflictPoolHeaderDataSize \
+       ((Size)MAXALIGN(sizeof(RWConflictPoolHeaderData)))
+
+
 /*
  * The SERIALIZABLEXIDTAG struct identifies an xid assigned to a serializable
  * transaction or any of its subtransactions.