Dont FATAL a IDLE IN TRANSACTIOn backend during conflict feature/hs-idle-query-cancellation
authorAndres Freund <[email protected]>
Sat, 13 Feb 2010 21:30:41 +0000 (22:30 +0100)
committerAndres Freund <[email protected]>
Sat, 13 Feb 2010 21:42:06 +0000 (22:42 +0100)
resolution. Instead avoid sending the error (and a later "read for
query") to the client to avoid confusing the client state.

src/backend/tcop/postgres.c

index fc9ff3cdd87feca63adb9009467c0248339d4b4b..8c0c8b9c88d1c58a30400b0b55c4a77a868b857a 100644 (file)
@@ -176,6 +176,12 @@ static int UseNewLine = 0;     /* Use EOF as query delimiters */
 static bool RecoveryConflictPending = false;
 static ProcSignalReason    RecoveryConflictReason;
 
+/*
+ * Are we disallowed from sending a "ready for query" message right
+ * now because it would confuse the frontend?
+ */
+bool silent_error_while_idle = false;
+
 /* ----------------------------------------------------------------
  *     decls for routines only used in this file
  * ----------------------------------------------------------------
@@ -2931,18 +2937,26 @@ ProcessInterrupts(void)
            RecoveryConflictPending = false;
            DisableNotifyInterrupt();
            DisableCatchupInterrupt();
-           if (DoingCommandRead)
-               ereport(FATAL,
-                       (errcode(ERRCODE_ADMIN_SHUTDOWN),
-                        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
+           if (DoingCommandRead){
+               /*
+                * We cant issue a normal ERROR here because the
+                * client doesnt expect the server to send an error at
+                * that point.
+                * We also may not send a "ready for query"/Z message
+                * because that would be unexpected as well.
+                */
+               silent_error_while_idle = true;
+               ereport(ERROR | LOG_NO_CLIENT,
+                       (errcode(ERRCODE_QUERY_CANCELED),
+                        errmsg("canceling statement due to conflict with recovery"),
+                        errdetail_recovery_conflict()));
+           }
+           else{
                ereport(ERROR,
                        (errcode(ERRCODE_QUERY_CANCELED),
                         errmsg("canceling statement due to conflict with recovery"),
                         errdetail_recovery_conflict()));
+           }
        }
 
        /*
@@ -3781,7 +3795,7 @@ PostgresMain(int argc, char *argv[], const char *username)
         * processing of batched messages, and because we don't want to report
         * uncommitted updates (that confuses autovacuum).
         */
-       if (send_ready_for_query)
+       if (send_ready_for_query && !silent_error_while_idle)
        {
            if (IsAbortedTransactionBlockState())
            {
@@ -3805,6 +3819,15 @@ PostgresMain(int argc, char *argv[], const char *username)
            send_ready_for_query = false;
        }
 
+       /*
+        * After ReadCommand continued we are sure that the frontend
+        * sent a message and can handle a 'Z' message.  We cant set
+        * that after ReadCommand because that will error out when in
+        * an aborted transaction - which we are when
+        * silent_error_while_idle was set.
+        */
+       silent_error_while_idle = false;
+
        /*
         * (2) Allow asynchronous signals to be executed immediately if they
         * come in while we are waiting for client input. (This must be