From 66035734ec36e56e36d94f413f5eb7d498993b6d Mon Sep 17 00:00:00 2001
From: Simon Riggs <simon@2ndQuadrant.com>
Date: Wed, 12 May 2010 19:45:02 +0000
Subject: [PATCH] Give most recovery conflict errors a retryable error code.
 From recent requests and discussions with Yeb Havinga and Kevin Grittner.

---
 src/backend/tcop/postgres.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b1d700a97e4..4a08888561e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.593 2010/04/20 01:38:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.594 2010/05/12 19:45:02 sriggs Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -174,6 +174,7 @@ static int	UseNewLine = 0;		/* Use EOF as query delimiters */
 
 /* whether or not, and why, we were cancelled by conflict with recovery */
 static bool RecoveryConflictPending = false;
+static bool RecoveryConflictRetryable = true;
 static ProcSignalReason RecoveryConflictReason;
 
 /* ----------------------------------------------------------------
@@ -2836,6 +2837,15 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
 
 		Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
 
+		/*
+		 * All conflicts apart from database cause dynamic errors where the
+		 * command or transaction can be retried at a later point with some
+		 * potential for success. No need to reset this, since
+		 * non-retryable conflict errors are currently FATAL.
+		 */
+		if (reason == PROCSIG_RECOVERY_CONFLICT_DATABASE)
+			RecoveryConflictRetryable = false;
+
 		/*
 		 * If it's safe to interrupt, and we're waiting for input or a lock,
 		 * service the interrupt immediately
@@ -2885,6 +2895,11 @@ ProcessInterrupts(void)
 			ereport(FATAL,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
 					 errmsg("terminating autovacuum process due to administrator command")));
+		else if (RecoveryConflictPending && RecoveryConflictRetryable)
+			ereport(FATAL,
+					(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
+			  errmsg("terminating connection due to conflict with recovery"),
+					 errdetail_recovery_conflict()));
 		else if (RecoveryConflictPending)
 			ereport(FATAL,
 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
@@ -2936,14 +2951,14 @@ ProcessInterrupts(void)
 			DisableCatchupInterrupt();
 			if (DoingCommandRead)
 				ereport(FATAL,
-						(errcode(ERRCODE_ADMIN_SHUTDOWN),
+						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 						 errmsg("terminating connection due to conflict with recovery"),
 						 errdetail_recovery_conflict(),
 				 errhint("In a moment you should be able to reconnect to the"
 						 " database and repeat your command.")));
 			else
 				ereport(ERROR,
-						(errcode(ERRCODE_QUERY_CANCELED),
+						(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
 				 errmsg("canceling statement due to conflict with recovery"),
 						 errdetail_recovery_conflict()));
 		}
-- 
GitLab