From 115d5d428a6c7dac8d5d99ff441a91f9988ec793 Mon Sep 17 00:00:00 2001
From: Andrey <amborodin@acm.org>
Date: Fri, 20 Dec 2019 09:36:20 +0500
Subject: [PATCH] Disallow cancelation of syncronous commit V1

Currently we allow to cancel awaiting of syncronous commit.
Some drivers cancel query after timeout. If application will retry
idempotent query, it will get confirmation of written data.
This can lead to split-brain in HA scenarios. To prevent it this
we add synchronous_commit_cancelation setting disalowing cancelation
of syncronous replication wait.
---
 src/backend/access/transam/xact.c |  1 +
 src/backend/replication/syncrep.c | 45 ++++++++++++++++++++++---------
 src/backend/utils/misc/guc.c      |  9 +++++++
 src/include/access/xact.h         |  3 +++
 4 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 5353b6ab0b..844ce36d79 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -80,6 +80,7 @@ bool		DefaultXactDeferrable = false;
 bool		XactDeferrable;
 
 int			synchronous_commit = SYNCHRONOUS_COMMIT_ON;
+bool		synchronous_commit_cancelation = true;
 
 /*
  * When running as a parallel worker, we place only a single
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 7bf7a1b7f8..96f0d5e8e5 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -250,13 +250,23 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 		 */
 		if (ProcDiePending)
 		{
-			ereport(WARNING,
-					(errcode(ERRCODE_ADMIN_SHUTDOWN),
-					 errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
-					 errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
-			whereToSendOutput = DestNone;
-			SyncRepCancelWait();
-			break;
+			if (synchronous_commit_cancelation)
+			{
+				ereport(WARNING,
+						(errcode(ERRCODE_ADMIN_SHUTDOWN),
+						errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
+						errdetail("The transaction has already committed locally, but might not have been replicated to the standby.")));
+				whereToSendOutput = DestNone;
+				SyncRepCancelWait();
+				break;
+			}
+			else
+			{
+				ereport(PANIC,
+						(errcode(ERRCODE_ADMIN_SHUTDOWN),
+						errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command"),
+						errdetail("The transaction has already committed locally and might not have been replicated to the standby.")));
+			}
 		}
 
 		/*
@@ -267,12 +277,21 @@ 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.")));
-			SyncRepCancelWait();
-			break;
+			if (synchronous_commit_cancelation)
+			{
+				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.")));
+				SyncRepCancelWait();
+				break;
+			}
+			else
+			{
+				ereport(WARNING,
+						(errmsg("canceling wait for synchronous replication due requested, but cancelation is not allowed"),
+						 errdetail("The transaction has already committed locally and might not have been replicated to the standby. We must wait here.")));
+			}
 		}
 
 		/*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 8d951ce404..c30aad97b3 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1222,6 +1222,15 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"synchronous_commit_cancelation", PGC_USERSET, WAL_SETTINGS,
+			gettext_noop("Allow to cancel waiting for replication of transaction commited localy."),
+			NULL
+		},
+		&synchronous_commit_cancelation,
+		true, NULL, NULL, NULL
+	},
+
 	{
 		{"log_checkpoints", PGC_SIGHUP, LOGGING_WHAT,
 			gettext_noop("Logs each checkpoint."),
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 9d2899dea1..5443c25edf 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -81,6 +81,9 @@ typedef enum
 /* Synchronous commit level */
 extern int	synchronous_commit;
 
+/* Allow cancelation of queries waiting for sync replication but commited locally */
+extern bool synchronous_commit_cancelation;
+
 /*
  * Miscellaneous flag bits to record events which occur on the top level
  * transaction. These flags are only persisted in MyXactFlags and are intended
-- 
2.20.1

