From aa9037f71f87bea43e4fd402c4de62c463212cd8 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Fri, 2 May 2025 17:02:11 +0500
Subject: [PATCH 2/3] Do not allow to cancel locally written transaction

This extends startup_synchronous_commit on locally logged transactions.
If transaction is not replicated and startup synchronisation is
configured, we do not allow transaction cancelation.

With extending GUC scope we also rename it to
failover_synchronous_standby_level.
---
 src/backend/access/transam/xact.c   |  2 +-
 src/backend/replication/syncrep.c   | 16 +++++++++++-----
 src/backend/utils/init/postinit.c   |  2 +-
 src/backend/utils/misc/guc_tables.c |  4 ++--
 src/include/access/xact.h           |  2 +-
 5 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8962b311361..a2011e80b40 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -85,7 +85,7 @@ bool		DefaultXactDeferrable = false;
 bool		XactDeferrable;
 
 int			synchronous_commit = SYNCHRONOUS_COMMIT_ON;
-int			startup_synchronous_commit = SYNCHRONOUS_COMMIT_OFF;
+int			failover_synchronous_standby_level = SYNCHRONOUS_COMMIT_OFF;
 
 /*
  * CheckXidAlive is a xid value pointing to a possibly ongoing (sub)
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 0c09f8060a3..c3077966917 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -318,11 +318,17 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		if (QueryCancelPending)
 		{
 			QueryCancelPending = false;
-			ereport(WARNING,
-					(errmsg("canceling wait for synchronous replication due to user request"),
-					 errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
+			if (failover_synchronous_standby_level < SYNCHRONOUS_COMMIT_REMOTE_WRITE)
+			{
+				ereport(WARNING,
+						(errmsg("canceling wait for synchronous replication due to user request"),
+						 errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
 			SyncRepCancelWait();
 			break;
+			}
+			ereport(WARNING,
+				(errmsg("user requested cancel of waiting for synchronous replication"),
+				 errdetail("The transaction has already committed locally and cannot be rolled back, but have not been replicated according to synchronous_standby_names.")));
 		}
 
 		/*
@@ -1150,7 +1156,7 @@ StartupSyncRepEstablished(void)
 	XLogRecPtr replication_lsn = 0;
 	int mode;
 
-	switch (startup_synchronous_commit)
+	switch (failover_synchronous_standby_level)
 	{
 		case SYNCHRONOUS_COMMIT_REMOTE_WRITE:
 			mode = SYNC_REP_WAIT_WRITE;
@@ -1162,7 +1168,7 @@ StartupSyncRepEstablished(void)
 			mode = SYNC_REP_WAIT_APPLY;
 			break;
 		default:
-		/* If startup_synchronous_commit is not set to a synchronous level, no need to check */
+		/* If failover_synchronous_standby_level is not set to a synchronous level, no need to check */
 			return true;
 	}
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 10b354fe069..cd84ddab0ae 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1222,7 +1222,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	{
 		ereport(FATAL,
 				(errcode(ERRCODE_CANNOT_CONNECT_NOW),
-				 errmsg("cannot connect until synchronous replication is established with standbys according to startup_synchronous_standby_level")));
+				 errmsg("cannot connect until synchronous replication is established with standbys according to failover_synchronous_standby_level")));
 	}
 
 	/*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 23e20261013..a95fbb1efe6 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -5174,11 +5174,11 @@ struct config_enum ConfigureNamesEnum[] =
 	},
 
 	{
-		{"startup_synchronous_standby_level", PGC_SUSET, REPLICATION_PRIMARY,
+		{"failover_synchronous_standby_level", PGC_SUSET, REPLICATION_PRIMARY,
 			gettext_noop("Sets the synchronization level neccesary to start node."),
 			NULL
 		},
-		&startup_synchronous_commit,
+		&failover_synchronous_standby_level,
 		SYNCHRONOUS_COMMIT_OFF, synchronous_commit_options,
 		NULL, NULL, NULL
 	},
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index feb47c80dfe..b29a8a94156 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -81,7 +81,7 @@ typedef enum
 
 /* Synchronous commit level */
 extern PGDLLIMPORT int synchronous_commit;
-extern PGDLLIMPORT int startup_synchronous_commit;
+extern PGDLLIMPORT int failover_synchronous_standby_level;
 
 /* used during logical streaming of a transaction */
 extern PGDLLIMPORT TransactionId CheckXidAlive;
-- 
2.39.5 (Apple Git-154)

