From: Andres Freund Date: Fri, 21 May 2010 18:17:11 +0000 (+0200) Subject: Implement cancellation of backends in IDLE IN TRANSACTION state. X-Git-Url: http://waps.l3s.uni-hannover.de/gitweb/?a=commitdiff_plain;h=06541b25fc11a8f17ec401de5a17eeae1bad57d1;p=users%2Fandresfreund%2Fpostgres.git Implement cancellation of backends in IDLE IN TRANSACTION state. Not having support for this was the reason for HS FATALing backends which had a lock conflict for longer than max_standby_(archive|standby)_delay as it couldnt cancel them without them loosing sync with the frontend. As this is not the case anymore fail the transaction "silently". Possibly one day the protocol can cope with this... --- diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index cba90a9e72..505d1363a6 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -177,6 +177,12 @@ static bool RecoveryConflictPending = false; static bool RecoveryConflictRetryable = true; static ProcSignalReason RecoveryConflictReason; +/* + * Are we disallowed from sending a "ready for query" message right + * now because it would confuse the frontend? + */ +static bool silent_error_while_idle = false; + /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- @@ -2877,6 +2883,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason) void ProcessInterrupts(void) { + int error = ERROR; + /* OK to accept interrupt now? */ if (InterruptHoldoffCount != 0 || CritSectionCount != 0) return; @@ -2949,18 +2957,24 @@ ProcessInterrupts(void) RecoveryConflictPending = false; DisableNotifyInterrupt(); DisableCatchupInterrupt(); - if (DoingCommandRead) - ereport(FATAL, - (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_T_R_SERIALIZATION_FAILURE), - errmsg("canceling statement due to conflict with recovery"), - errdetail_recovery_conflict())); + + 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; + error |= LOG_NO_CLIENT; + + } + + ereport(error, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("canceling statement due to conflict with recovery"), + errdetail_recovery_conflict())); } /* @@ -2968,15 +2982,18 @@ ProcessInterrupts(void) * request --- sending an extra error message won't accomplish * anything. Otherwise, go ahead and throw the error. */ - if (!DoingCommandRead) + if (DoingCommandRead) { - ImmediateInterruptOK = false; /* not idle anymore */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - ereport(ERROR, - (errcode(ERRCODE_QUERY_CANCELED), - errmsg("canceling statement due to user request"))); + silent_error_while_idle = true; + error |= LOG_NO_CLIENT; } + + ImmediateInterruptOK = false; /* not idle anymore */ + DisableNotifyInterrupt(); + DisableCatchupInterrupt(); + ereport(error, + (errcode(ERRCODE_QUERY_CANCELED), + errmsg("canceling statement due to user request"))); } /* If we get here, do nothing (probably, QueryCancelPending was reset) */ } @@ -3814,11 +3831,13 @@ PostgresMain(int argc, char *argv[], const char *username) set_ps_display("idle", false); pgstat_report_activity(""); } - - ReadyForQuery(whereToSendOutput); - send_ready_for_query = false; + if(!silent_error_while_idle){ + ReadyForQuery(whereToSendOutput); + send_ready_for_query = false; + } } + /* * (2) Allow asynchronous signals to be executed immediately if they * come in while we are waiting for client input. (This must be @@ -3854,6 +3873,23 @@ PostgresMain(int argc, char *argv[], const char *username) if (ignore_till_sync && firstchar != EOF) continue; + if(silent_error_while_idle && !doing_extended_query_message) + { + /* + * When using the simple protocol: + * At this point we have processed a ReadCommand which means + * that we got some input from the frontend and thus can start + * spewing errors again + + * When using the extended protocol: + * doing_extended_query_message is false after a 'sync' + * message - which is exactly the point after which we can + * start sending errors again. + */ + silent_error_while_idle = false; + } + + switch (firstchar) { case 'Q': /* simple query */