From 75a220fc3a4919e4e6fa188515f80082b9dacd09 Mon Sep 17 00:00:00 2001
From: Kirk Jamison <k.jamison@jp.fujitsu.com>
Date: Wed, 28 Oct 2020 06:32:01 +0000
Subject: [PATCH v28 4/4] TRUNCATE optimization.

---
 src/backend/storage/buffer/bufmgr.c | 74 +++++++++++++++++++++++++++++++++++--
 src/backend/storage/smgr/smgr.c     | 14 +++----
 src/include/storage/bufmgr.h        |  2 +-
 3 files changed, 79 insertions(+), 11 deletions(-)

diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 18d8512..e1c1190 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3121,17 +3121,37 @@ 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	firstDelBlocks[MAX_FORKNUM + 1];
+	BlockNumber	nblocks;
+	BlockNumber	nBlocksToInvalidate;
+	ForkNumber	forks[MAX_FORKNUM + 1];
+	bool		accurate;
 	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++)
+	{
+		RelFileNodeBackend rnode = smgr_reln[i]->smgr_rnode;
+
+		rnodes[i] = rnode;
+	}
+
+	/* non-local relations */
+	rels = (SMgrRelation *)palloc(nnodes * sizeof(SMgrRelation));
+	nodes = palloc(sizeof(RelFileNode) * nnodes);
 
 	/* If it's a local relation, it's localbuf.c's problem. */
 	for (i = 0; i < nnodes; i++)
@@ -3142,7 +3162,10 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 				DropRelFileNodeAllLocalBuffers(rnodes[i].node);
 		}
 		else
+		{
+			rels[n] = smgr_reln[i];
 			nodes[n++] = rnodes[i].node;
+		}
 	}
 
 	/*
@@ -3152,10 +3175,54 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 	if (n == 0)
 	{
 		pfree(nodes);
+		pfree(rels);
+		pfree(rnodes);
 		return;
 	}
 
 	/*
+	 * Zero the array of blocks because these will all be dropped anyway.
+	 * Get the total number of blocks for a given fork per relation.
+	 * Give up the optimization if the block count of any fork of relation
+	 * cannot be trusted.
+	 */
+	memset(firstDelBlocks, 0, sizeof(firstDelBlocks));
+	for (i = 0; i < n; i++)
+	{
+		nforks = 0;
+		nBlocksToInvalidate = 0;
+
+		for (j = 0; j <= MAX_FORKNUM; j++)
+		{
+			if (!smgrexists(rels[i], j))
+				continue;
+
+			/* Get the number of blocks for a relation's fork */
+			nblocks = smgrnblocks(rels[i], j, &accurate);
+
+			if (!accurate)
+				break;
+
+			nBlocksToInvalidate += nblocks;
+
+			forks[nforks++] = j;
+		}
+		if (!accurate || j >= MAX_FORKNUM ||
+			nBlocksToInvalidate >= BUF_DROP_FULL_SCAN_THRESHOLD)
+		{
+			goto buffer_full_scan;
+			return;
+		}
+
+		DropRelFileNodeBuffers(rels[i], forks, nforks, firstDelBlocks);
+	}
+	pfree(nodes);
+	pfree(rels);
+	pfree(rnodes);
+	return;
+
+buffer_full_scan:
+	/*
 	 * 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
 	 * an exactly determined value, as it depends on many factors (CPU and RAM
@@ -3208,8 +3275,9 @@ DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes)
 		else
 			UnlockBufHdr(bufHdr, buf_state);
 	}
-
 	pfree(nodes);
+	pfree(rels);
+	pfree(rnodes);
 }
 
 /* ---------------------------------------------------------------------
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 6b8528e..fd2cf84 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

