diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
old mode 100644
new mode 100755
index 2fd32e0..d734626
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -7,12 +7,27 @@
  * "hint" status bits if we see that the inserting or deleting transaction
  * has now committed or aborted (and it is safe to set the hint bits).
  * If the hint bits are changed, SetBufferCommitInfoNeedsSave is called on
  * the passed-in buffer.  The caller must hold not only a pin, but at least
  * shared buffer content lock on the buffer containing the tuple.
  *
+ * Hint bits are maintained directly on the tuple itself and on a separate
+ * in-memory cache.  If the hint bit is found from the cache, the hint bit
+ * is set on the tuple but the page is not marked dirty.  This is done to
+ * reduce i/o resulting from hint bit only changes.  The cache itself is
+ * exactly like the clog page, and uses a similar page size, but does not
+ * have to.  Cache maintenance occurs every Nth (typically 100) cache miss
+ * that successfully fetches the transaction status from clog.
+ *
+ * It's tempting to try and expand the simple last transaction status in
+ * transam.c but this check happens too late in the critical visibility
+ * routines to provide much benefit.  For the fastest possible cache
+ * performance with the least amount of impact on scan heavy cases, you have
+ * to maintain the cache here if you want to avoid dirtying the page
+ * based on interaction with the cache.
+ *
  * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array)
  * before TransactionIdDidCommit/TransactionIdDidAbort (which look in
  * pg_clog).  Otherwise we have a race condition: we might decide that a
  * just-committed transaction crashed, because none of the tests succeed.
  * xact.c is careful to record commit/abort in pg_clog before it unsets
  * MyProc->xid in PGPROC array.  That fixes that problem, but it also
@@ -69,26 +84,297 @@
 /* Static variables representing various special snapshot semantics */
 SnapshotData SnapshotNowData = {HeapTupleSatisfiesNow};
 SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};
 SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};
 SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};
 
+/* hint bit cache definitions and structures */
+
+#define HBCACHE_PAGESZ 8192
+/* 8192 bytes of stororage in each hint bit cache page */
+#define HBCACHE_PAGE_COUNT 4		/* how many pages to keep */
+#define HBCACHE_ROLLUPSZ 100
+/* 100 cache misses are stored in array before re-filling cache */
+#define HBCACHE_HIT_THRESHOLD 5
+/* cache pagees with less than 5 interesting transactions per cache re-fill
+ * are not considered interesting
+ */
+
+#define HBCACHE_BITS_PER_XACT	2
+#define HBCACHE_XACTS_PER_BYTE	4
+#define HBCACHE_XACTS_PER_PAGE	(HBCACHE_PAGESZ * HBCACHE_XACTS_PER_BYTE)
+#define HBCACHE_XACT_BITMASK	((1 << HBCACHE_BITS_PER_XACT) - 1)
+
+#define HBCACHE_COMITTED 1
+#define HBCACHE_INVALID  2
+
+#define HBCache_TransactionIdToPage(xid)	((xid) / (TransactionId) HBCACHE_XACTS_PER_PAGE)
+#define HBCache_TransactionIdToPgIndex(xid) ((xid) % (TransactionId) HBCACHE_XACTS_PER_PAGE)
+#define HBCache_TransactionIdToByte(xid)	(HBCache_TransactionIdToPgIndex(xid) / HBCACHE_XACTS_PER_BYTE)
+#define HBCache_TransactionIdToBIndex(xid)	((xid) % (TransactionId) HBCACHE_XACTS_PER_BYTE)
+
+
+/* describes a hint bit page in memory */
+typedef struct
+{
+	int XidPage;  /* this page describes a range of transaction ids */
+	int HitCount;	/* number of cache hits on this page */
+	bool Clear;
+	/* during rollup, signals the page is changing and cache data needs to
+	 * be cleared
+	 */
+} HintBitCachePageHeader;
+
 /* local functions */
 static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
 
+static HintBitCachePageHeader HintbitCachePageList[HBCACHE_PAGE_COUNT]
+	= {{-1, 0, false}, {-1, 0, false}, {-1, 0, false}, {-1, 0, false}};
+static char HintBitCachePageList[HBCACHE_PAGE_COUNT][HBCACHE_PAGESZ];
+static int HintBitCachePageRollupList[HBCACHE_ROLLUPSZ];
+static int HintBitCacheMissCount = 0;
+
+/* qsort comparison function */
+static int
+int_cmp(const void *p1, const void *p2)
+{
+	int v1 = *((const int *) p1);
+	int v2 = *((const int *) p2);
+
+	if (v1 < v2)
+		return -1;
+	if (v1 > v2)
+		return 1;
+	return 0;
+}
+
+/*
+ * RollUpHintBitCache()
+ *
+ * Whenever a transaction is checked against the hint bit cache and the status
+ * is not there (a cache miss), the transaction's page is added to the
+ * rollup array. When that array fills, the rollup array is processed into
+ * a new set of cache pages.
+ *
+ * The algorithm is to sort the rollup array and iterate it counting the
+ * number of identical pages as we go.  When we hit a new page (or the
+ * end of the array) and the number of counted pages is over a minimum
+ * threshold, the page is moved into the cache if it has more cache hits
+ * than any page that is already in the cache.
+ */
+static void RollUpHintBitCache()
+{
+	TransactionId LastPage = HintBitCachePageRollupList[0];
+	/* The page we are duplicate tracking. */
+
+	int PageIdx;
+	int HitCount = 0;
+	/* effectively initializes to 1: the first iteration of the loop will
+	 * always match LastPage and increment this value
+	 */
+
+	int Page;
+	int PageMinHits = HBCACHE_ROLLUPSZ + 1;
+	/* The smallest number of hits for a page currently in the cache. It's
+	 * scanned comparing as we go, so initialize it to an impossibly high
+	 * value here */
+
+	int PageMinIdx;
+	/* The cache page index of the page with the smallest number of hits.
+	 * if a new cache page higher number of hits with this one, it is
+	 * swapped in its place.
+	 */
+	int XidPage;
+
+	bool AlreadyInCache;
+
+	/* sort the list in order to make counting identical pages easier */
+	qsort(HintBitCachePageRollupList, HBCACHE_ROLLUPSZ, sizeof(int), int_cmp);
+
+	for (PageIdx = 0; PageIdx < HBCACHE_ROLLUPSZ; PageIdx++)
+	{
+		/* If page is the same as the last one, increment the hitcount
+		 * or reset it and do cache management based on the number of hits
+		 */
+		XidPage = HintBitCachePageRollupList[PageIdx];
+
+		if(XidPage == LastPage)
+		        HitCount++;
+		else
+		{
+			/* Now check the page represented by LastPage to see if its
+			 * interesting for cache purposes.  To avoid thrashing cache pages
+			 * in and out, a minimum threshold of hits has to be met before
+			 * putting a page in the cache.
+			 */
+			if (HitCount >= HBCACHE_HIT_THRESHOLD)
+			{
+CheckLastXidBlock:
+				/* Look through the pages, find the one with the smallest
+				 * hit count, and save off the index.
+				 */
+				AlreadyInCache = false;
+
+				for (Page = 0; Page < HBCACHE_PAGE_COUNT; Page++)
+				{
+					if (HintbitCachePageList[Page].HitCount < PageMinHits)
+					{
+						PageMinIdx = Page;
+						PageMinHits = HintbitCachePageList[Page].HitCount;
+					}
+
+					/* If this page is alredy in the cache, we can leave it
+					 * an without clearing it.
+					 */
+					if (HintbitCachePageList[Page].XidPage == LastPage)
+						AlreadyInCache = true;
+				}
+
+				/* does the page we are examining have a higher number of
+				 * cache hits than any page currently in the cache?
+				 */
+				if (!AlreadyInCache && HitCount > PageMinHits)
+				{
+					/* mark the cache buffer with the new page, hint count
+					 * and clear flag.
+					 */
+					HintbitCachePageList[PageMinIdx].XidPage = LastPage;
+					HintbitCachePageList[PageMinIdx].HitCount = HitCount;
+					HintbitCachePageList[PageMinIdx].Clear = true;
+				}
+			}
+
+			/* reset the hit count, since we've already technically already
+			 * seen this page in this iteration, HitCount is initialized
+			 * to 1, and this page is now the one tracked via LastPage.
+			 */
+			HitCount = 1;
+			LastPage = XidPage;
+		}
+	}
+
+	/* When the loop is exited, the last block of identical pages may have
+	 * never gotten checked via the XidPage == LastPage condition.
+	 * Sneakily jump back into the loop if necessary.
+	 *
+	 * jump back into the loop to check it but decrement PageIdx first so
+	 * that the final LastPage assignment does't look outside the array.
+	 */
+	if(HitCount >= HBCACHE_HIT_THRESHOLD)
+	{
+		/* Since HitCount is reset when a page is processed, checking it
+		 * is enough to guarantee the last page in the loop was never
+		 * processed.
+		 */
+		goto CheckLastXidBlock;
+	}
+
+	/* Cache pages that are new have to be cleared.  Look for ones marked as
+	 * such and reset the hit count for all pages.
+	 */
+	for (Page = 0; Page < HBCACHE_PAGE_COUNT; Page++)
+	{
+		if(HintbitCachePageList[PageMinIdx].Clear)
+			memset(HintBitCachePageList[Page], 0, HBCACHE_PAGESZ);
+
+		HintbitCachePageList[PageMinIdx].Clear = 0;
+		HintbitCachePageList[PageMinIdx].HitCount = 0;
+	}
+
+	HintBitCacheMissCount = 0;
+	return;
+}
+
+/*
+ * HintBitCacheXidStatus()
+ *
+ * Compute the page for a TransactionId, and compare it against the pages
+ * currently in the cache.  If the page is found, resolve the hint bit
+ * status's position in the block and return it.
+ */
+static inline int
+HintBitCacheXidStatus(TransactionId xid)
+{
+	int XidPage = HBCache_TransactionIdToPage(xid);
+	int Page;
+
+	for(Page = 0; Page < HBCACHE_PAGE_COUNT; Page++)
+	{
+		/* is the transaction status in the range defined by the page? */
+		if(HintbitCachePageList[Page].XidPage == XidPage)
+		{
+			int	byteno = HBCache_TransactionIdToByte(xid);
+			int bshift = HBCache_TransactionIdToBIndex(xid) * HBCACHE_BITS_PER_XACT;
+			char *byteptr = HintBitCachePageList[Page] + byteno;
+
+			return (*byteptr >> bshift) & HBCACHE_XACT_BITMASK;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * SetXidInHintBitCache()
+ *
+ * Compute the page for a TransactionId, and compare it against the pages
+ * currently in the cache.  If the page is found, compute the bit position
+ * and set it to 1 (true).  If it is not found, put the TransactionId in the
+ * array that holds cache misses.  If that array fills, rebuild the cache.
+ */
+static inline void
+SetXidInHintBitCache(TransactionId xid, int hbcache_status)
+{
+	int XidPage = HBCache_TransactionIdToPage(xid);
+	int Page;
+
+	for(Page = 0; Page < HBCACHE_PAGE_COUNT; Page++)
+	{
+	    if(XidPage == HintbitCachePageList[Page].XidPage)
+	    {
+			int	byteno = HBCache_TransactionIdToByte(xid);
+			int bshift = HBCache_TransactionIdToBIndex(xid) * HBCACHE_BITS_PER_XACT;
+			char *byteptr = HintBitCachePageList[Page] + byteno;
+			char byteval = *byteptr;
+
+			byteval &= ~(((1 << HBCACHE_BITS_PER_XACT) - 1) << bshift);
+			byteval |= (hbcache_status << bshift);
+			*byteptr = byteval;
+
+			/* there is a minute chance of overflow here, but it doesn't
+			 * seem worthwhile to test for it
+			 */
+			HintbitCachePageList[Page].HitCount++;
+			break;
+		}
+	}
+
+	HintBitCachePageRollupList[HintBitCacheMissCount++] = XidPage;
+
+	/* Enough transactions have fallen through the cache that it's time to
+	 * rebuild it.  Hopefully we don't have to do this too often
+	 */
+	if (HintBitCacheMissCount == HBCACHE_ROLLUPSZ)
+		RollUpHintBitCache();
+
+	return;
+}
+
 
 /*
  * SetHintBits()
  *
  * Set commit/abort hint bits on a tuple, if appropriate at this time.
  *
  * It is only safe to set a transaction-committed hint bit if we know the
  * transaction's commit record has been flushed to disk.  We cannot change
  * the LSN of the page here because we may hold only a share lock on the
  * buffer, so we can't use the LSN to interlock this; we have to just refrain
  * from setting the hint bit until some future re-examination of the tuple.
+ * The ability of the hint bit to be set is returned to the caller (the hint
+ * bit cache only saves safe bits in the cache to avoid repeated checks).
  *
  * We can always set hint bits when marking a transaction aborted.	(Some
  * code in heapam.c relies on that!)
  *
  * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then
  * we can always set the hint bits, since pre-9.0 VACUUM FULL always used
@@ -101,27 +387,35 @@ static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
  * Normal commits may be asynchronous, so for those we need to get the LSN
  * of the transaction and then check whether this is flushed.
  *
  * The caller should pass xid as the XID of the transaction to check, or
  * InvalidTransactionId if no check is needed.
  */
-static inline void
+static inline bool
 SetHintBits(HeapTupleHeader tuple, Buffer buffer,
 			uint16 infomask, TransactionId xid)
 {
 	if (TransactionIdIsValid(xid))
 	{
 		/* NB: xid must be known committed here! */
 		XLogRecPtr	commitLSN = TransactionIdGetCommitLSN(xid);
 
 		if (XLogNeedsFlush(commitLSN))
-			return;				/* not flushed yet, so don't set hint */
+			return false;			/* not flushed yet, so don't set hint */
 	}
 
 	tuple->t_infomask |= infomask;
 	SetBufferCommitInfoNeedsSave(buffer);
+	return true;
+}
+
+static inline void
+SetHintBitsCache(HeapTupleHeader tuple, Buffer buffer,
+				uint16 infomask, TransactionId xid)
+{
+	tuple->t_infomask |= infomask;
 }
 
 /*
  * HeapTupleSetHintBits --- exported version of SetHintBits()
  *
  * This must be separate because of C99's brain-dead notions about how to
@@ -911,91 +1205,127 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot,
 bool
 HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot,
 					   Buffer buffer)
 {
 	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
 	{
+		int hbcache_status;
+
 		if (tuple->t_infomask & HEAP_XMIN_INVALID)
 			return false;
 
-		/* Used by pre-9.0 binary upgrades */
-		if (tuple->t_infomask & HEAP_MOVED_OFF)
-		{
-			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+		hbcache_status = HintBitCacheXidStatus(HeapTupleHeaderGetXmin(tuple));
 
-			if (TransactionIdIsCurrentTransactionId(xvac))
-				return false;
-			if (!TransactionIdIsInProgress(xvac))
-			{
-				if (TransactionIdDidCommit(xvac))
-				{
-					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
-								InvalidTransactionId);
-					return false;
-				}
-				SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
-							InvalidTransactionId);
-			}
+		if (hbcache_status == HBCACHE_COMITTED)
+		{
+			/* The hint bit was not set on the tuple, but it was set in the
+			 * cache.  Since cache bits are only set if the transaction's
+			 * commit record has been flushed to disk, we can call a quicker
+			 * version of SetHintBits that does not check the commit record
+			 * and, because the page is not marked dirty, does not have to
+			 * lock the buffer.
+			 */
+	        SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED,
+                                HeapTupleHeaderGetXmin(tuple));
 		}
-		/* Used by pre-9.0 binary upgrades */
-		else if (tuple->t_infomask & HEAP_MOVED_IN)
+		else if (hbcache_status == HBCACHE_INVALID)
 		{
-			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+	        SetHintBitsCache(tuple, buffer, HEAP_XMIN_INVALID,
+                                HeapTupleHeaderGetXmin(tuple));
 
-			if (!TransactionIdIsCurrentTransactionId(xvac))
+			return false;
+		}
+		else
+		{
+			/* Used by pre-9.0 binary upgrades */
+			if (tuple->t_infomask & HEAP_MOVED_OFF)
 			{
-				if (TransactionIdIsInProgress(xvac))
+				TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+				if (TransactionIdIsCurrentTransactionId(xvac))
 					return false;
-				if (TransactionIdDidCommit(xvac))
+				if (!TransactionIdIsInProgress(xvac))
+				{
+					if (TransactionIdDidCommit(xvac))
+					{
+						SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+									InvalidTransactionId);
+						return false;
+					}
 					SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
 								InvalidTransactionId);
-				else
+				}
+			}
+			/* Used by pre-9.0 binary upgrades */
+			else if (tuple->t_infomask & HEAP_MOVED_IN)
+			{
+				TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+
+				if (!TransactionIdIsCurrentTransactionId(xvac))
 				{
-					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
-								InvalidTransactionId);
-					return false;
+					if (TransactionIdIsInProgress(xvac))
+						return false;
+					if (TransactionIdDidCommit(xvac))
+						SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+									InvalidTransactionId);
+					else
+					{
+						SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+									InvalidTransactionId);
+						return false;
+					}
 				}
 			}
-		}
-		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
-		{
-			if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
-				return false;	/* inserted after scan started */
+			else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
+			{
+				if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+					return false;	/* inserted after scan started */
 
-			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
-				return true;
+				if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
+					return true;
 
-			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
-				return true;
+				if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */
+					return true;
 
-			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
+				Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
 
-			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
-			{
-				/* deleting subtransaction must have aborted */
-				SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
-							InvalidTransactionId);
-				return true;
+				if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))
+				{
+					/* deleting subtransaction must have aborted */
+					SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
+								InvalidTransactionId);
+					return true;
+				}
+
+				if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+					return true;	/* deleted after scan started */
+				else
+					return false;	/* deleted before scan started */
 			}
+			else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
+				return false;
+			else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
+			{
+				/* Set the hint bit on the tuple, and put it in the cache
+				 * if it was actually set.
+				 */
+				if(SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
+							HeapTupleHeaderGetXmin(tuple)))
+					SetXidInHintBitCache(HeapTupleHeaderGetXmin(tuple),
+								HBCACHE_COMITTED);
 
-			if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
-				return true;	/* deleted after scan started */
+			}
 			else
-				return false;	/* deleted before scan started */
-		}
-		else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))
-			return false;
-		else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))
-			SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
-						HeapTupleHeaderGetXmin(tuple));
-		else
-		{
-			/* it must have aborted or crashed */
-			SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
-						InvalidTransactionId);
-			return false;
+			{
+				/* it must have aborted or crashed */
+				if(SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,
+							InvalidTransactionId))
+					SetXidInHintBitCache(HeapTupleHeaderGetXmin(tuple),
+								HBCACHE_INVALID);
+				return false;
+			}
 		}
 	}
 
 	/*
 	 * By here, the inserting transaction has committed - have to check
 	 * when...
