From 5beaa843a9c96e36b75c32cd6efba49fbe5b48cd Mon Sep 17 00:00:00 2001
From: Kirk Jamison <k.jamison@jp.fujitsu.com>
Date: Thu, 26 Nov 2020 02:42:58 +0000
Subject: [PATCH v32 4/4] Optimize DropRelFileNodesAllBuffers() in recovery.

DropRelFileNodesAllBuffers() is optimized to skip the time-consuming
scan of the whole buffer pool during recovery when the relation is
small enough, or when the number of blocks to be invalidated is below
the full scan threshold. This improves the DropRelationFiles()
performance when the TRUNCATE command truncated off any of the empty
pages at the end of relation, and when dropping relation buffers if a
commit/rollback transaction has been prepared in FinishPreparedTransaction().
---
 src/backend/storage/buffer/bufmgr.c | 95 ++++++++++++++++++++++++++++++++++++-
 src/backend/storage/smgr/smgr.c     | 14 +++---
 src/include/storage/bufmgr.h        |  2 +-
 3 files changed, 101 insertions(+), 10 deletions(-)

diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index f354eb4..143f592 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3089,17 +3089,32 @@ DropRelFileNodeBuffers(SMgrRelation smgr_reln, ForkNumber *forkNum,
  * --------------------------------------------------------------------
  */
 void
-DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
+DropRelFileNodesAllBuffers(SMgrRelation *smgr_reln, int nnodes)
 {
 	int			i,
+				j,
+				*nforks,
 				n = 0;
+	SMgrRelation	*rels;
+	RelFileNodeBackend *rnodes;
 	RelFileNode *nodes;
+	BlockNumber	**blocks;
+	BlockNumber	*firstDelBlocks;
+	BlockNumber	nBlocksToInvalidate = 0;
+	ForkNumber	**forks;
 	bool		use_bsearch;
 
 	if (nnodes == 0)
 		return;
 
-	nodes = palloc(sizeof(RelFileNode) * nnodes);	/* non-local relations */
+	/* Create an array which contains all relations to be dropped */
+	rnodes = palloc(sizeof(RelFileNodeBackend) * nnodes);
+	for (i = 0; i < nnodes; i++)
+		rnodes[i] = smgr_reln[i]->smgr_rnode;
+
+	/* non-local relations */
+	rels = palloc(sizeof(SMgrRelation) * nnodes);
+	nodes = palloc(sizeof(RelFileNode) * nnodes);
 
 	/* If it's a local relation, it's localbuf.c's problem. */
 	for (i = 0; i < nnodes; i++)
@@ -3110,7 +3125,10 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 				DropRelFileNodeAllLocalBuffers(rnodes[i].node);
 		}
 		else
+		{
+			rels[n] = smgr_reln[i];
 			nodes[n++] = rnodes[i].node;
+		}
 	}
 
 	/*
@@ -3120,6 +3138,68 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 	if (n == 0)
 	{
 		pfree(nodes);
+		pfree(rels);
+		pfree(rnodes);
+		return;
+	}
+
+	nforks = palloc(sizeof(int) * n);
+	forks = palloc(sizeof(ForkNumber *) * n);
+	blocks = palloc(sizeof(BlockNumber *) * n);
+	firstDelBlocks = palloc(sizeof(BlockNumber) * n * (MAX_FORKNUM + 1));
+	for (i = 0; i < n; i++)
+	{
+		forks[i] = palloc(sizeof(ForkNumber) * (MAX_FORKNUM + 1));
+		blocks[i] = palloc(sizeof(BlockNumber) * (MAX_FORKNUM + 1));
+	}
+
+	for (i = 0; i < n; i++)
+	{
+		int		numForks = 0;
+
+		for (j = 0; j <= MAX_FORKNUM; j++)
+		{
+			if (!smgrexists(rels[i], j))
+				continue;
+
+			/* Get the number of blocks for a relation's fork */
+			blocks[i][numForks] = smgrnblocks(rels[i], j, NULL);
+
+			nBlocksToInvalidate += blocks[i][numForks];
+
+			forks[i][numForks++] = j;
+		}
+		nforks[i] = numForks;
+	}
+
+	/* Zero the array of blocks because these will all be dropped anyway */
+	MemSet(firstDelBlocks, 0, sizeof(BlockNumber) * n * (MAX_FORKNUM + 1));
+
+	/*
+	 * We enter the optimization iff we are in recovery and the number of blocks to
+	 * be invalidated for all relations do not exceed the threshold for full scan.
+	 * Otherwise, we proceed to full scan of the whole buffer pool.
+	 */
+	if (InRecovery && nBlocksToInvalidate < BUF_DROP_FULL_SCAN_THRESHOLD)
+	{
+		for (j = 0; j < n; j++)
+		{
+			FindAndDropRelFileNodeBuffers(nodes[j], forks[j], nforks[j],
+										  blocks[j], firstDelBlocks);
+		}
+
+		for (j = 0; j < n; j++)
+		{
+			pfree(blocks[j]);
+			pfree(forks[j]);
+		}
+		pfree(firstDelBlocks);
+		pfree(blocks);
+		pfree(forks);
+		pfree(nforks);
+		pfree(nodes);
+		pfree(rels);
+		pfree(rnodes);
 		return;
 	}
 
@@ -3177,7 +3257,18 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 			UnlockBufHdr(bufHdr, buf_state);
 	}
 
+	for (i = 0; i < n; i++)
+	{
+		pfree(blocks[i]);
+		pfree(forks[i]);
+	}
+	pfree(firstDelBlocks);
+	pfree(blocks);
+	pfree(forks);
+	pfree(nforks);
 	pfree(nodes);
+	pfree(rels);
+	pfree(rnodes);
 }
 
 /* ---------------------------------------------------------------------
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 9d3a67c..3663bb7 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -392,7 +392,13 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
 		return;
 
 	/*
-	 * create an array which contains all relations to be dropped, and close
+	 * 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
 	 */
 	rnodes = palloc(sizeof(RelFileNodeBackend) * nrels);
@@ -409,12 +415,6 @@ smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo)
 	}
 
 	/*
-	 * 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.
 	 */
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 056f65e..2e5189b 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -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) \
-- 
1.8.3.1

