diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c index da28e21..33fc022 100644 --- a/src/backend/access/nbtree/nbtxlog.c +++ b/src/backend/access/nbtree/nbtxlog.c @@ -423,6 +423,15 @@ btree_xlog_vacuum(XLogReaderState *record) for (blkno = xlrec->lastBlockVacuumed + 1; blkno < thisblkno; blkno++) { /* + * First we try to know if the page is already in buffer cache. + * If not, it is definitely unpinned. If it's already there, + * we read it. + * + */ + if (!BufferInCache(thisrnode, MAIN_FORKNUM, blkno)) + continue; + + /* * We use RBM_NORMAL_NO_LOG mode because it's not an error * condition to see all-zero pages. The original btvacuumpage * scan would have skipped over all-zero pages, noting them in FSM @@ -430,10 +439,6 @@ btree_xlog_vacuum(XLogReaderState *record) * throw an error here. (We could skip acquiring the cleanup lock * if PageIsNew, but it's probably not worth the cycles to test.) * - * XXX we don't actually need to read the block, we just need to - * confirm it is unpinned. If we had a special call into the - * buffer manager we could optimise this so that if the block is - * not in shared_buffers we confirm it as unpinned. */ buffer = XLogReadBufferExtended(thisrnode, MAIN_FORKNUM, blkno, RBM_NORMAL_NO_LOG); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index cd3aaad..eb74da3 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -3640,3 +3640,34 @@ rnode_comparator(const void *p1, const void *p2) else return 0; } + +/* + * BufferInCache -- returns true if buffer is already in buffer cache and + * false otherwise. + * + * No locks are held either at entry or exit. + */ +bool +BufferInCache(RelFileNode rnode, ForkNumber forkNum, BlockNumber blockNum) +{ + BufferTag Tag; /* identity of requested block */ + uint32 Hash; /* hash value for newTag */ + LWLock *PartitionLock; /* buffer partition lock for it */ + int buf_id; + + SMgrRelation smgr = smgropen(rnode, InvalidBackendId); + + /* create a tag so we can lookup the buffer */ + INIT_BUFFERTAG(Tag, smgr->smgr_rnode.node, forkNum, blockNum); + + /* determine its hash code and partition lock ID */ + Hash = BufTableHashCode(&Tag); + PartitionLock = BufMappingPartitionLock(Hash); + + /* see if the block is in the buffer pool already */ + LWLockAcquire(PartitionLock, LW_SHARED); + buf_id = BufTableLookup(&Tag, Hash); + LWLockRelease(PartitionLock); + + return buf_id != -1; +} diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index ec0a254..ced564f 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -159,6 +159,8 @@ extern void MarkBufferDirty(Buffer buffer); extern void IncrBufferRefCount(Buffer buffer); extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum); +extern bool BufferInCache(RelFileNode rnode, ForkNumber forkNum, + BlockNumber blockNum); extern void InitBufferPool(void); extern void InitBufferPoolAccess(void);