Add prefetch support for the undo log
authorDilip Kumar <[email protected]>
Wed, 24 Apr 2019 09:06:28 +0000 (14:36 +0530)
committerThomas Munro <[email protected]>
Wed, 17 Jul 2019 06:55:35 +0000 (18:55 +1200)
Add prefetching function for undo smgr and also provide mechanism
to prefetch without relcache.

Dilip Kumar

src/backend/postmaster/pgstat.c
src/backend/storage/buffer/bufmgr.c
src/backend/storage/smgr/undofile.c
src/include/pgstat.h
src/include/storage/bufmgr.h

index c742861dae8949b62cd7c96db7ba4e43750fb84c..d8dc0cc547f502af1104ff258a8c0c11bfda4da4 100644 (file)
@@ -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;
index 2b1d60680e8622c8c93a9e8787df18d2d3784799..5abf42efae550ed556ea0b139c6cf49b6d5dd812 100644 (file)
@@ -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 */
 }
 
 
index 04d4514e35649cd4ecba96f375b328fd3a50a126..3be0f5e922d6aee51e4ba2438e362247c3d5d1ac 100644 (file)
@@ -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
index 1936c5db6fe8c81e18aec82aa4571617b07faa5e..2fff6734fc330265d96f75a820d3f9890844135b 100644 (file)
@@ -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,
index a04190aa92419e6fb5ef13481e13f2f35f947ba0..5c0ed5859111725824786fd1a78877b11bb04f56 100644 (file)
@@ -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,