diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c old mode 100644 new mode 100755 index 2fd32e0..62ed2fe --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -10,6 +10,21 @@ * 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 @@ -64,6 +79,7 @@ #include "storage/bufmgr.h" #include "storage/procarray.h" #include "utils/tqual.h" +#include "utils/memutils.h" /* Static variables representing various special snapshot semantics */ @@ -72,9 +88,332 @@ 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 1 +#define HBCACHE_XACTS_PER_BYTE 8 +#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 +/* The hint bit cache only tracks commit bits */ + + +#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 +{ + TransactionId XidPage; /* this page describes a range of transaction ids */ + int HitCount; /* number of cache hits on this page */ + bool Clear; + char *Data; + /* 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 inline void SetHintBitsNonDirty(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid); + +static HintBitCachePageHeader *HintBitCachePageList = NULL; +static char *HintBitCachePageData = NULL; +static TransactionId *HintBitCachePageRollupList = NULL; +static int HintBitCacheMissCount = 0; + +/* qsort comparison function */ +static int +xid_cmp(const void *p1, const void *p2) +{ + TransactionId v1 = *((const TransactionId *) p1); + TransactionId v2 = *((const TransactionId *) p2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; +} + +/* + * InitializeHintbitCache + * Initialize the hint bit cache. + */ +static void +InitializeHintBitCache(void) +{ + int cachepage; + int cachesz = sizeof(char) * HBCACHE_PAGE_COUNT * HBCACHE_PAGESZ; + + HintBitCachePageData = MemoryContextAlloc(CacheMemoryContext, cachesz); + MemSet(HintBitCachePageData, 0, cachesz); + + HintBitCachePageList = MemoryContextAlloc(CacheMemoryContext, + sizeof(HintBitCachePageHeader) * HBCACHE_PAGE_COUNT); + + for(cachepage = 0; cachepage < HBCACHE_PAGE_COUNT; cachepage++) + { + /* initialize cache page to a valid that is impossible to see in the + * wild -- since bits are always divided out to calculate a page, + * a value with the top bits set is impossible to see and cant't + * represent an invalid page. + */ + TransactionId invalid_page = (TransactionId) 0xFFFFFFFF; + + Assert(sizeof(TransactionId) == 4); + + HintBitCachePageList[cachepage].XidPage = invalid_page; + HintBitCachePageList[cachepage].HitCount = 0; + HintBitCachePageList[cachepage].Clear = false; + HintBitCachePageList[cachepage].Data = + &HintBitCachePageData[HBCACHE_PAGESZ * cachepage]; + } + + + HintBitCachePageRollupList = MemoryContextAlloc(CacheMemoryContext, + sizeof(int) * HBCACHE_ROLLUPSZ); +} + +/* + * 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. + */ + TransactionId XidPage; + + bool AlreadyInCache; + + /* sort the list in order to make counting identical pages easier */ + qsort(HintBitCachePageRollupList, HBCACHE_ROLLUPSZ, sizeof(int), xid_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[PageMinIdx].Data, 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(HeapTupleHeader tuple, Buffer buffer, + uint16 infomask, TransactionId xid) +{ + TransactionId XidPage = HBCache_TransactionIdToPage(xid); + int Page; + + if(HintBitCachePageList == NULL) + InitializeHintBitCache(); + + 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].Data + byteno; + + int hbcache_status = (*byteptr >> bshift) & HBCACHE_XACT_BITMASK; + + if (hbcache_status == HBCACHE_COMITTED) + SetHintBitsNonDirty(tuple, buffer, infomask, xid); + + return hbcache_status; + } + } + + 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) +{ + TransactionId XidPage = HBCache_TransactionIdToPage(xid); + int Page; + + Assert(HintBitCachePageList != NULL); + + 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].Data + 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() @@ -122,6 +461,51 @@ SetHintBits(HeapTupleHeader tuple, Buffer buffer, } /* + * SetHintBitsCache() + * + * Do the same action as SetHintBits(), but also set the commit bit in the + * cache. Since only commit bits are cached, that is what they are assumed + * to be. + * + */ +static inline void +SetHintBitsCache(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 */ + } + + tuple->t_infomask |= infomask; + SetBufferCommitInfoNeedsSave(buffer); + + SetXidInHintBitCache(xid, HBCACHE_COMITTED); + + return; +} + +/* + * SetHintBitsNonDirty() + * + * Set the hint bits on the tuple only but don't dirty the page. This is done + * when a hit bint is found in the cache. There's no reason not to set it + * anyways, because if the page is dirty for another reason it will get + * written out. + * + */ +static inline void +SetHintBitsNonDirty(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 @@ -162,7 +546,9 @@ HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer, bool HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) { - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; @@ -229,7 +615,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { @@ -245,7 +631,9 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; - if (tuple->t_infomask & HEAP_XMAX_COMMITTED) + if (tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return true; @@ -286,7 +674,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) return true; } - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetXmax(tuple)); return false; } @@ -335,7 +723,9 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) bool HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) { - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; @@ -408,7 +798,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { @@ -424,7 +814,9 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; - if (tuple->t_infomask & HEAP_XMAX_COMMITTED) + if (tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return true; @@ -468,7 +860,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) return true; } - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetXmax(tuple)); return false; } @@ -501,7 +893,9 @@ bool HeapTupleSatisfiesToast(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) { - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; @@ -535,7 +929,7 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple, Snapshot snapshot, if (TransactionIdIsInProgress(xvac)) return false; if (TransactionIdDidCommit(xvac)) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, InvalidTransactionId); else { @@ -582,7 +976,9 @@ HTSU_Result HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, Buffer buffer) { - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return HeapTupleInvisible; @@ -655,7 +1051,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return HeapTupleInvisible; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { @@ -671,7 +1067,9 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return HeapTupleMayBeUpdated; - if (tuple->t_infomask & HEAP_XMAX_COMMITTED) + if (tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return HeapTupleMayBeUpdated; @@ -720,7 +1118,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, return HeapTupleMayBeUpdated; } - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetXmax(tuple)); return HeapTupleUpdated; /* updated by other */ } @@ -751,7 +1149,9 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, { snapshot->xmin = snapshot->xmax = InvalidTransactionId; - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; @@ -822,7 +1222,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, return true; /* in insertion by other */ } else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { @@ -838,7 +1238,9 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; - if (tuple->t_infomask & HEAP_XMAX_COMMITTED) + if (tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple))) { if (tuple->t_infomask & HEAP_IS_LOCKED) return true; @@ -882,7 +1284,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, return true; } - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, HeapTupleHeaderGetXmax(tuple)); return false; /* updated by other */ } @@ -912,7 +1314,9 @@ bool HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) { - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return false; @@ -985,8 +1389,14 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple))) return false; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, - HeapTupleHeaderGetXmin(tuple)); + { + /* Set the hint bit on the tuple, and put it in the cache + * if it was actually set. + */ + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)); + + } else { /* it must have aborted or crashed */ @@ -1016,7 +1426,9 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, return true; } - if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple)))) { if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) { @@ -1038,8 +1450,8 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, } /* xmax transaction committed */ - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple)); } /* @@ -1074,7 +1486,9 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, * If the inserting transaction aborted, then the tuple was never visible * to any other transaction, so we can delete it immediately. */ - if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMIN_COMMITTED, + HeapTupleHeaderGetXmin(tuple)))) { if (tuple->t_infomask & HEAP_XMIN_INVALID) return HEAPTUPLE_DEAD; @@ -1125,7 +1539,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, return HEAPTUPLE_DELETE_IN_PROGRESS; } else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple))) - SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED, + SetHintBitsCache(tuple, buffer, HEAP_XMIN_COMMITTED, HeapTupleHeaderGetXmin(tuple)); else { @@ -1192,13 +1606,17 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, return HEAPTUPLE_LIVE; } - if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) + if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED + || HintBitCacheXidStatus(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple)))) { if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) return HEAPTUPLE_DELETE_IN_PROGRESS; else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) - SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + { + SetHintBitsCache(tuple, buffer, HEAP_XMAX_COMMITTED, + HeapTupleHeaderGetXmax(tuple)); + } else { /* @@ -1208,7 +1626,6 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, InvalidTransactionId); return HEAPTUPLE_LIVE; } - /* * At this point the xmax is known committed, but we might not have * been able to set the hint bit yet; so we can no longer Assert that