commit 91f2000b3ddf3e8ff136db0d9ad21721fd2f7e47 Author: mithun Date: Tue Dec 6 01:03:24 2016 +0530 meta page cache rebase diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c index 44332e7..0c9c48a 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -478,7 +478,7 @@ _hash_metapinit(Relation rel, double num_tuples, ForkNumber forkNum) buf = _hash_getnewbuf(rel, BUCKET_TO_BLKNO(metap, i), forkNum); pg = BufferGetPage(buf); pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg); - pageopaque->hasho_prevblkno = InvalidBlockNumber; + pageopaque->hasho_prevblkno = metap->hashm_maxbucket; pageopaque->hasho_nextblkno = InvalidBlockNumber; pageopaque->hasho_bucket = i; pageopaque->hasho_flag = LH_BUCKET_PAGE; @@ -885,7 +885,7 @@ _hash_splitbucket(Relation rel, * operation end, we clear split-in-progress flag. */ oopaque->hasho_flag |= LH_BUCKET_BEING_SPLIT; - + oopaque->hasho_prevblkno = maxbucket; npage = BufferGetPage(nbuf); /* @@ -893,7 +893,7 @@ _hash_splitbucket(Relation rel, * split is in progress. */ nopaque = (HashPageOpaque) PageGetSpecialPointer(npage); - nopaque->hasho_prevblkno = InvalidBlockNumber; + nopaque->hasho_prevblkno = maxbucket; nopaque->hasho_nextblkno = InvalidBlockNumber; nopaque->hasho_bucket = nbucket; nopaque->hasho_flag = LH_BUCKET_PAGE | LH_BUCKET_BEING_POPULATED; diff --git a/src/backend/access/hash/hashsearch.c b/src/backend/access/hash/hashsearch.c index 8d43b38..8323f60 100644 --- a/src/backend/access/hash/hashsearch.c +++ b/src/backend/access/hash/hashsearch.c @@ -152,6 +152,11 @@ _hash_readprev(IndexScanDesc scan, _hash_relbuf(rel, *bufp); *bufp = InvalidBuffer; + + /* If it is a bucket page there will not be a prevblkno. */ + if ((*opaquep)->hasho_flag & LH_BUCKET_PAGE) + return; + /* check for interrupts while we're not holding any buffer lock */ CHECK_FOR_INTERRUPTS(); if (BlockNumberIsValid(blkno)) @@ -216,10 +221,8 @@ _hash_first(IndexScanDesc scan, ScanDirection dir) uint32 hashkey; Bucket bucket; BlockNumber blkno; - BlockNumber oldblkno = InvalidBuffer; - bool retry = false; Buffer buf; - Buffer metabuf; + Buffer metabuf = InvalidBuffer; Page page; HashPageOpaque opaque; HashMetaPage metap; @@ -277,10 +280,26 @@ _hash_first(IndexScanDesc scan, ScanDirection dir) so->hashso_sk_hash = hashkey; - /* Read the metapage */ - metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); - page = BufferGetPage(metabuf); - metap = HashPageGetMeta(page); + if (rel->rd_amcache != NULL) + { + metap = (HashMetaPage)rel->rd_amcache; + } + else + { + /* Read the metapage */ + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); + page = BufferGetPage(metabuf); + metap = HashPageGetMeta(page); + + /* Cache the metapage data for next time*/ + rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt, + sizeof(HashMetaPageData)); + memcpy(rel->rd_amcache, metap, sizeof(HashMetaPageData)); + metap = (HashMetaPage)rel->rd_amcache; + + /* Release metapage lock, but keep pin. */ + _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK); + } /* * Loop until we get a lock on the correct target bucket. @@ -290,46 +309,50 @@ _hash_first(IndexScanDesc scan, ScanDirection dir) /* * Compute the target bucket number, and convert to block number. */ - bucket = _hash_hashkey2bucket(hashkey, - metap->hashm_maxbucket, + bucket = _hash_hashkey2bucket(hashkey, metap->hashm_maxbucket, metap->hashm_highmask, metap->hashm_lowmask); blkno = BUCKET_TO_BLKNO(metap, bucket); - /* Release metapage lock, but keep pin. */ - _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK); + /* Fetch the primary bucket page for the bucket */ + buf = _hash_getbuf(rel, blkno, HASH_READ, LH_BUCKET_PAGE); + page = BufferGetPage(buf); + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + Assert(opaque->hasho_bucket == bucket); - /* - * If the previous iteration of this loop locked what is still the - * correct target bucket, we are done. Otherwise, drop any old lock - * and lock what now appears to be the correct bucket. + /* Check if this bucket is split after we have cached the metapage. + * To do this we need to check whether cached maxbucket number is less + * than or equal to maxbucket number stored in bucket page, which was + * set with that times maxbucket number during bucket page splits. + * In case of upgrade hashno_prevblkno of old bucket page will be set + * with InvalidBlockNumber. And as of now maximum value the + * hashm_maxbucket can take is 1 less than InvalidBlockNumber + * (see _hash_expandtable). So an explicit check for InvalidBlockNumber + * in hasho_prevblkno will tell whether current bucket has been split + * after metapage was cached. */ - if (retry) + if (opaque->hasho_prevblkno == InvalidBlockNumber || + opaque->hasho_prevblkno <= metap->hashm_maxbucket) { - if (oldblkno == blkno) - break; - _hash_relbuf(rel, buf); + /* Ok now we have the right bucket proceed to search in it. */ + break; } - /* Fetch the primary bucket page for the bucket */ - buf = _hash_getbuf(rel, blkno, HASH_READ, LH_BUCKET_PAGE); + /* Meta page cache is old try again updating it. */ + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); + page = BufferGetPage(metabuf); + metap = HashPageGetMeta(page); + memcpy(rel->rd_amcache, metap, sizeof(HashMetaPageData)); + metap = (HashMetaPage)rel->rd_amcache; - /* - * Reacquire metapage lock and check that no bucket split has taken - * place while we were awaiting the bucket lock. - */ - _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_READ); - oldblkno = blkno; - retry = true; + /* Release Meta page buffer lock, but keep pin. */ + _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK); } /* done with the metapage */ - _hash_dropbuf(rel, metabuf); - - page = BufferGetPage(buf); - opaque = (HashPageOpaque) PageGetSpecialPointer(page); - Assert(opaque->hasho_bucket == bucket); + if (!BufferIsInvalid(metabuf)) + _hash_dropbuf(rel, metabuf); so->hashso_bucket_buf = buf;