Optimize DropRelFileNodesAllBuffers() for recovery.
authorAmit Kapila <akapila@postgresql.org>
Wed, 13 Jan 2021 02:16:11 +0000 (07:46 +0530)
committerAmit Kapila <akapila@postgresql.org>
Wed, 13 Jan 2021 02:16:11 +0000 (07:46 +0530)
Similar to commit d6ad34f341, this patch optimizes
DropRelFileNodesAllBuffers() by avoiding the complete buffer pool scan and
instead find the buffers to be invalidated by doing lookups in the
BufMapping table.

This optimization helps operations where the relation files need to be
removed like Truncate, Drop, Abort of Create Table, etc.

Author: Kirk Jamison
Reviewed-by: Kyotaro Horiguchi, Takayuki Tsunakawa, and Amit Kapila
Tested-By: Haiying Tang
Discussion: https://postgr.es/m/OSBPR01MB3207DCA7EC725FDD661B3EDAEF660@OSBPR01MB3207.jpnprd01.prod.outlook.com

src/backend/storage/buffer/bufmgr.c
src/backend/storage/smgr/smgr.c
src/include/storage/bufmgr.h

index c192c2e35b5c398f3d236c872dedca44cae750cd..c46b8abad12e479b432490fd1d950ee295afa2dc 100644 (file)
@@ -3104,28 +3104,33 @@ DropRelFileNodeBuffers(SMgrRelation smgr_reln, ForkNumber *forkNum,
  * --------------------------------------------------------------------
  */
 void
-DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
+DropRelFileNodesAllBuffers(SMgrRelation *smgr_reln, int nnodes)
 {
-   int         i,
-               n = 0;
+   int         i;
+   int         j;
+   int         n = 0;
+   SMgrRelation *rels;
+   BlockNumber (*block)[MAX_FORKNUM + 1];
+   BlockNumber nBlocksToInvalidate = 0;
    RelFileNode *nodes;
+   bool        cached = true;
    bool        use_bsearch;
 
    if (nnodes == 0)
        return;
 
-   nodes = palloc(sizeof(RelFileNode) * nnodes);   /* non-local relations */
+   rels = palloc(sizeof(SMgrRelation) * nnodes);   /* non-local relations */
 
    /* If it's a local relation, it's localbuf.c's problem. */
    for (i = 0; i < nnodes; i++)
    {
-       if (RelFileNodeBackendIsTemp(rnodes[i]))
+       if (RelFileNodeBackendIsTemp(smgr_reln[i]->smgr_rnode))
        {
-           if (rnodes[i].backend == MyBackendId)
-               DropRelFileNodeAllLocalBuffers(rnodes[i].node);
+           if (smgr_reln[i]->smgr_rnode.backend == MyBackendId)
+               DropRelFileNodeAllLocalBuffers(smgr_reln[i]->smgr_rnode.node);
        }
        else
-           nodes[n++] = rnodes[i].node;
+           rels[n++] = smgr_reln[i];
    }
 
    /*
@@ -3134,10 +3139,72 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
     */
    if (n == 0)
    {
-       pfree(nodes);
+       pfree(rels);
        return;
    }
 
+   /*
+    * This is used to remember the number of blocks for all the relations
+    * forks.
+    */
+   block = (BlockNumber (*)[MAX_FORKNUM + 1])
+       palloc(sizeof(BlockNumber) * n * (MAX_FORKNUM + 1));
+
+   /*
+    * We can avoid scanning the entire buffer pool if we know the exact size
+    * of each of the given relation forks. See DropRelFileNodeBuffers.
+    */
+   for (i = 0; i < n && cached; i++)
+   {
+       for (j = 0; j <= MAX_FORKNUM; j++)
+       {
+           /* Get the number of blocks for a relation's fork. */
+           block[i][j] = smgrnblocks_cached(rels[i], j);
+
+           /* We need to only consider the relation forks that exists. */
+           if (block[i][j] == InvalidBlockNumber)
+           {
+               if (!smgrexists(rels[i], j))
+                   continue;
+               cached = false;
+               break;
+           }
+
+           /* calculate the total number of blocks to be invalidated */
+           nBlocksToInvalidate += block[i][j];
+       }
+   }
+
+   /*
+    * We apply the optimization iff the total number of blocks to invalidate
+    * is below the BUF_DROP_FULL_SCAN_THRESHOLD.
+    */
+   if (cached && nBlocksToInvalidate < BUF_DROP_FULL_SCAN_THRESHOLD)
+   {
+       for (i = 0; i < n; i++)
+       {
+           for (j = 0; j <= MAX_FORKNUM; j++)
+           {
+               /* ignore relation forks that doesn't exist */
+               if (!BlockNumberIsValid(block[i][j]))
+                   continue;
+
+               /* drop all the buffers for a particular relation fork */
+               FindAndDropRelFileNodeBuffers(rels[i]->smgr_rnode.node,
+                                             j, block[i][j], 0);
+           }
+       }
+
+       pfree(block);
+       pfree(rels);
+       return;
+   }
+
+   pfree(block);
+   nodes = palloc(sizeof(RelFileNode) * n);    /* non-local relations */
+   for (i = 0; i < n; i++)
+       nodes[i] = rels[i]->smgr_rnode.node;
+
    /*
     * For low number of relations to drop just use a simple walk through, to
     * save the bsearch overhead. The threshold to use is rather a guess than
@@ -3193,6 +3260,7 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
    }
 
    pfree(nodes);
+   pfree(rels);
 }
 
 /* ---------------------------------------------------------------------
index af603c3db3b3ba2505ffb95a0dcc51ffdbd33c06..4dc24649df982dbc187665b3d0d4d0fca74bd75a 100644 (file)
@@ -390,6 +390,12 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
    if (nrels == 0)
        return;
 
+   /*
+    * Get rid of any remaining buffers for the relations.  bufmgr will just
+    * drop them without bothering to write the contents.
+    */
+   DropRelFileNodesAllBuffers(rels, nrels);
+
    /*
     * create an array which contains all relations to be dropped, and close
     * each relation's forks at the smgr level while at it
@@ -407,12 +413,6 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
            smgrsw[which].smgr_close(rels[i], forknum);
    }
 
-   /*
-    * Get rid of any remaining buffers for the relations.  bufmgr will just
-    * drop them without bothering to write the contents.
-    */
-   DropRelFileNodesAllBuffers(rnodes, nrels);
-
    /*
     * It'd be nice to tell the stats collector to forget them immediately,
     * too. But we can't because we don't know the OIDs.
index 0c484f3addb925c5c4212f018093306bfa996fcd..fb00fda6a7f21fbcc4f1a5337a64ff2643dafb6d 100644 (file)
@@ -205,7 +205,7 @@ extern void FlushRelationsAllBuffers(struct SMgrRelationData **smgrs, int nrels)
 extern void FlushDatabaseBuffers(Oid dbid);
 extern void DropRelFileNodeBuffers(struct SMgrRelationData *smgr_reln, ForkNumber *forkNum,
                                   int nforks, BlockNumber *firstDelBlock);
-extern void DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes);
+extern void DropRelFileNodesAllBuffers(struct SMgrRelationData **smgr_reln, int nnodes);
 extern void DropDatabaseBuffers(Oid dbid);
 
 #define RelationGetNumberOfBlocks(reln) \