From 8e9190898eb0cd12afea72f7ad89c192d5943e60 Mon Sep 17 00:00:00 2001
From: Dilip Kumar <dilipkumar@localhost.localdomain>
Date: Wed, 24 Apr 2019 14:36:28 +0530
Subject: [PATCH 05/13] Add prefetch support for the undo log

Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar
---
 src/backend/postmaster/pgstat.c     |   3 ++
 src/backend/storage/buffer/bufmgr.c | 101 ++++++++++++++++++++++++------------
 src/backend/storage/smgr/undofile.c |  13 ++++-
 src/include/pgstat.h                |   1 +
 src/include/storage/bufmgr.h        |   4 ++
 5 files changed, 87 insertions(+), 35 deletions(-)

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c742861..d8dc0cc 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w)
 		case WAIT_EVENT_UNDO_CHECKPOINT_SYNC:
 			event_name = "UndoCheckpointSync";
 			break;
+		case WAIT_EVENT_UNDO_FILE_PREFETCH:
+			event_name = "UndoFilePrefetch";
+			break;
 		case WAIT_EVENT_UNDO_FILE_READ:
 			event_name = "UndoFileRead";
 			break;
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 2b1d606..5abf42e 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -520,14 +520,57 @@ ComputeIoConcurrency(int io_concurrency, double *target)
 	return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX);
 }
 
+#ifdef USE_PREFETCH
 /*
- * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ * PrefetchBufferGuts -- Guts of prefetching a buffer.
  *
  * This is named by analogy to ReadBuffer but doesn't actually allocate a
  * buffer.  Instead it tries to ensure that a future ReadBuffer for the given
  * block will not be delayed by the I/O.  Prefetching is optional.
  * No-op if prefetching isn't compiled in.
  */
+static void
+PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum,
+				   BlockNumber blockNum)
+{
+	BufferTag	newTag;		/* identity of requested block */
+	uint32		newHash;	/* hash value for newTag */
+	LWLock	   *newPartitionLock;	/* buffer partition lock for it */
+	int			buf_id;
+
+	/* create a tag so we can lookup the buffer */
+	INIT_BUFFERTAG(newTag, rnode, forkNum, blockNum);
+
+	/* determine its hash code and partition lock ID */
+	newHash = BufTableHashCode(&newTag);
+	newPartitionLock = BufMappingPartitionLock(newHash);
+
+	/* see if the block is in the buffer pool already */
+	LWLockAcquire(newPartitionLock, LW_SHARED);
+	buf_id = BufTableLookup(&newTag, newHash);
+	LWLockRelease(newPartitionLock);
+
+	/* If not in buffers, initiate prefetch */
+	if (buf_id < 0)
+		smgrprefetch(smgr, forkNum, blockNum);
+
+	/*
+	 * If the block *is* in buffers, we do nothing.  This is not really
+	 * ideal: the block might be just about to be evicted, which would be
+	 * stupid since we know we are going to need it soon.  But the only
+	 * easy answer is to bump the usage_count, which does not seem like a
+	 * great solution: when the caller does ultimately touch the block,
+	 * usage_count would get bumped again, resulting in too much
+	 * favoritism for blocks that are involved in a prefetch sequence. A
+	 * real fix would involve some additional per-buffer state, and it's
+	 * not clear that there's enough of a problem to justify that.
+	 */
+}
+#endif							/* USE_PREFETCH */
+
+/*
+ * PrefetchBuffer -- initiate asynchronous read of a block of a relation
+ */
 void
 PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 {
@@ -550,42 +593,32 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum);
 	}
 	else
-	{
-		BufferTag	newTag;		/* identity of requested block */
-		uint32		newHash;	/* hash value for newTag */
-		LWLock	   *newPartitionLock;	/* buffer partition lock for it */
-		int			buf_id;
-
-		/* create a tag so we can lookup the buffer */
-		INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node,
-					   forkNum, blockNum);
-
-		/* determine its hash code and partition lock ID */
-		newHash = BufTableHashCode(&newTag);
-		newPartitionLock = BufMappingPartitionLock(newHash);
-
-		/* see if the block is in the buffer pool already */
-		LWLockAcquire(newPartitionLock, LW_SHARED);
-		buf_id = BufTableLookup(&newTag, newHash);
-		LWLockRelease(newPartitionLock);
+		PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr,
+						   forkNum, blockNum);
+#endif							/* USE_PREFETCH */
+}
 
-		/* If not in buffers, initiate prefetch */
-		if (buf_id < 0)
-			smgrprefetch(reln->rd_smgr, forkNum, blockNum);
+/*
+ * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a
+ *									relcache entry for the relation.
+ */
+void
+PrefetchBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
+							  BlockNumber blockNum, char relpersistence)
+{
+#ifdef USE_PREFETCH
+	SMgrRelation smgr = smgropen(rnode,
+								 relpersistence == RELPERSISTENCE_TEMP
+								 ? MyBackendId : InvalidBackendId);
 
-		/*
-		 * If the block *is* in buffers, we do nothing.  This is not really
-		 * ideal: the block might be just about to be evicted, which would be
-		 * stupid since we know we are going to need it soon.  But the only
-		 * easy answer is to bump the usage_count, which does not seem like a
-		 * great solution: when the caller does ultimately touch the block,
-		 * usage_count would get bumped again, resulting in too much
-		 * favoritism for blocks that are involved in a prefetch sequence. A
-		 * real fix would involve some additional per-buffer state, and it's
-		 * not clear that there's enough of a problem to justify that.
-		 */
+	if (relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* pass it off to localbuf.c */
+		LocalPrefetchBuffer(smgr, forkNum, blockNum);
 	}
-#endif							/* USE_PREFETCH */
+	else
+		PrefetchBufferGuts(rnode, smgr, forkNum, blockNum);
+#endif						/* USE_PREFETCH */
 }
 
 
diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c
index 04d4514..3be0f5e 100644
--- a/src/backend/storage/smgr/undofile.c
+++ b/src/backend/storage/smgr/undofile.c
@@ -119,7 +119,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum,
 void
 undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum)
 {
-	elog(ERROR, "undofile_prefetch is not supported");
+#ifdef USE_PREFETCH
+	File		file;
+	off_t		seekpos;
+
+	Assert(forknum == MAIN_FORKNUM);
+	file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE);
+	seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE));
+
+	Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE);
+
+	(void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH);
+#endif							/* USE_PREFETCH */
 }
 
 void
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 1936c5d..2fff673 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -937,6 +937,7 @@ typedef enum
 	WAIT_EVENT_UNDO_CHECKPOINT_READ,
 	WAIT_EVENT_UNDO_CHECKPOINT_SYNC,
 	WAIT_EVENT_UNDO_CHECKPOINT_WRITE,
+	WAIT_EVENT_UNDO_FILE_PREFETCH,
 	WAIT_EVENT_UNDO_FILE_READ,
 	WAIT_EVENT_UNDO_FILE_WRITE,
 	WAIT_EVENT_UNDO_FILE_FLUSH,
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index a04190a..5c0ed58 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -165,6 +165,10 @@ extern PGDLLIMPORT int32 *LocalRefCount;
 extern bool ComputeIoConcurrency(int io_concurrency, double *target);
 extern void PrefetchBuffer(Relation reln, ForkNumber forkNum,
 						   BlockNumber blockNum);
+extern void PrefetchBufferWithoutRelcache(RelFileNode rnode,
+										  ForkNumber forkNum,
+										  BlockNumber blockNum,
+										  char relpersistence);
 extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
 								 BlockNumber blockNum, ReadBufferMode mode,
-- 
1.8.3.1

