From 9600d3e13f90ffb0930d9a833072d15184ac342f Mon Sep 17 00:00:00 2001
From: Xiaoran Wang <wxiaoran@vmware.com>
Date: Tue, 5 Dec 2023 15:11:22 +0800
Subject: [PATCH v2] Not to invalidate CatalogSnapshot for local invalidation
 messages

For local invalidation messages, there is no need to call
`InvalidateCatalogSnapshot` to set the CatalogSnapshot to NULL and
rebuild it later. Instead, just update the CatalogSnapshot's curcid
in `SnapshotSetCommandId`, this way can make the CatalogSnapshot work
well too.

This optimization can reduce the overhead of rebuilding CatalogSnapshot
after each command.
---
 src/backend/utils/cache/inval.c  | 47 ++++++++++++++++++++++++--------
 src/backend/utils/time/snapmgr.c |  6 ++--
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index a041d7d604..8b7e3903c6 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -273,6 +273,11 @@ static struct RELCACHECALLBACK
 
 static int	relcache_callback_count = 0;
 
+
+static inline void LocalExecuteInvalidationMessageIternal(SharedInvalidationMessage *msg,
+								bool localMsg);
+static void LocalExecuteLocalInvalidationMessage(SharedInvalidationMessage *msg);
+
 /* ----------------------------------------------------------------
  *				Invalidation subgroup support functions
  * ----------------------------------------------------------------
@@ -696,20 +701,21 @@ InvalidateSystemCachesExtended(bool debug_discard)
 }
 
 /*
- * LocalExecuteInvalidationMessage
+ * LocalExecuteInvalidationMessageInternal
  *
  * Process a single invalidation message (which could be of any type).
  * Only the local caches are flushed; this does not transmit the message
  * to other backends.
  */
-void
-LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
+static inline void
+LocalExecuteInvalidationMessageIternal(SharedInvalidationMessage *msg, bool localMsg)
 {
 	if (msg->id >= 0)
 	{
 		if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
 		{
-			InvalidateCatalogSnapshot();
+			if (!localMsg)
+				InvalidateCatalogSnapshot();
 
 			SysCacheInvalidate(msg->cc.id, msg->cc.hashValue);
 
@@ -720,7 +726,8 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
 	{
 		if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
 		{
-			InvalidateCatalogSnapshot();
+			if (!localMsg)
+				InvalidateCatalogSnapshot();
 
 			CatalogCacheFlushCatalog(msg->cat.catId);
 
@@ -769,15 +776,33 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
 	else if (msg->id == SHAREDINVALSNAPSHOT_ID)
 	{
 		/* We only care about our own database and shared catalogs */
-		if (msg->sn.dbId == InvalidOid)
-			InvalidateCatalogSnapshot();
-		else if (msg->sn.dbId == MyDatabaseId)
+		if ((msg->sn.dbId == InvalidOid || msg->sn.dbId == MyDatabaseId) && !localMsg)
 			InvalidateCatalogSnapshot();
 	}
 	else
 		elog(FATAL, "unrecognized SI message ID: %d", msg->id);
 }
 
+/*
+ * Execute InvalidationMessage from other backend
+ */
+
+void
+LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
+{
+	LocalExecuteInvalidationMessageIternal(msg, false);
+}
+
+/*
+ * Execute InvalidationMessage from local backend
+ */
+static void
+LocalExecuteLocalInvalidationMessage(SharedInvalidationMessage *msg)
+{
+	LocalExecuteInvalidationMessageIternal(msg, true);
+}
+
+
 /*
  *		InvalidateSystemCaches
  *
@@ -1055,7 +1080,7 @@ AtEOXact_Inval(bool isCommit)
 	else
 	{
 		ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
-									LocalExecuteInvalidationMessage);
+									LocalExecuteLocalInvalidationMessage);
 	}
 
 	/* Need not free anything explicitly */
@@ -1143,7 +1168,7 @@ AtEOSubXact_Inval(bool isCommit)
 	else
 	{
 		ProcessInvalidationMessages(&myInfo->PriorCmdInvalidMsgs,
-									LocalExecuteInvalidationMessage);
+									LocalExecuteLocalInvalidationMessage);
 
 		/* Pop the transaction state stack */
 		transInvalInfo = myInfo->parent;
@@ -1180,7 +1205,7 @@ CommandEndInvalidationMessages(void)
 		return;
 
 	ProcessInvalidationMessages(&transInvalInfo->CurrentCmdInvalidMsgs,
-								LocalExecuteInvalidationMessage);
+								LocalExecuteLocalInvalidationMessage);
 
 	/* WAL Log per-command invalidation messages for wal_level=logical */
 	if (XLogLogicalInfoActive())
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 9198850ad2..341d9efa63 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -80,7 +80,7 @@
  * instant, even in transaction-snapshot mode.  It should only be used for
  * special-purpose code (say, RI checking.)  CatalogSnapshot points to an
  * MVCC snapshot intended to be used for catalog scans; we must invalidate it
- * whenever a system catalog change occurs.
+ * whenever a system catalog change occurs in other backends.
  *
  * These SnapshotData structs are static to simplify memory allocation
  * (see the hack in GetSnapshotData to avoid repeated malloc/free).
@@ -462,6 +462,9 @@ InvalidateCatalogSnapshotConditionally(void)
 void
 SnapshotSetCommandId(CommandId curcid)
 {
+	if (CatalogSnapshot)
+		CatalogSnapshot->curcid = curcid;
+
 	if (!FirstSnapshotSet)
 		return;
 
@@ -469,7 +472,6 @@ SnapshotSetCommandId(CommandId curcid)
 		CurrentSnapshot->curcid = curcid;
 	if (SecondarySnapshot)
 		SecondarySnapshot->curcid = curcid;
-	/* Should we do the same with CatalogSnapshot? */
 }
 
 /*
-- 
2.39.2 (Apple Git-143)

