On 06/04/2014 01:38 AM, Josh Berkus wrote:
> On 06/03/2014 02:53 PM, Tom Lane wrote:
>> Josh Berkus <j...@agliodbs.com> writes:
>>> Out of curiosity, how much harder would it have been just to abort the
>>> transaction?  I think breaking the connection is probably the right
>>> behavior, but before folks start arguing it out, I wanted to know if
>>> aborting the transaction is even a reasonable thing to do.
>> FWIW, I think aborting the transaction is probably better, especially
>> if the patch is designed to do nothing to already-aborted transactions.
>> If the client is still there, it will see the abort as a failure in its
>> next query, which is less likely to confuse it completely than a
>> connection loss.  (I think, anyway.)
> Personally, I think we'll get about equal amounts of confusion either way.
>
>> The argument that we might want to close the connection to free up
>> connection slots doesn't seem to me to hold water as long as we allow
>> a client that *isn't* inside a transaction to sit on an idle connection
>> forever.  Perhaps there is room for a second timeout that limits how
>> long you can sit idle independently of being in a transaction, but that
>> isn't this patch.
> Like I said, I'm marginally in favor of terminating the connection, but
> I'd be completely satisfied with a timeout which aborted the transaction
> instead.  Assuming that change doesn't derail this patch and keep it
> from getting into 9.5, of course.

The change is as simple as changing the ereport from FATAL to ERROR.

Attached is a new patch doing it that way.

-- 
Vik

*** a/contrib/postgres_fdw/connection.c
--- b/contrib/postgres_fdw/connection.c
***************
*** 343,348 **** configure_remote_session(PGconn *conn)
--- 343,356 ----
  		do_sql_command(conn, "SET extra_float_digits = 3");
  	else
  		do_sql_command(conn, "SET extra_float_digits = 2");
+ 
+ 	/*
+ 	 * Ensure the remote server doesn't abort our transaction if we keep it idle
+ 	 * for too long.
+ 	 * XXX: The version number must be corrected prior to commit!
+ 	 */
+ 	if (remoteversion >= 90400)
+ 		do_sql_command(conn, "SET idle_in_transaction_timeout = 0");
  }
  
  /*
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 5545,5550 **** COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
--- 5545,5568 ----
        </listitem>
       </varlistentry>
  
+      <varlistentry id="guc-idle-in-transaction-timeout" xreflabel="idle_in_transaction_timeout">
+       <term><varname>idle_in_transaction_timeout</varname> (<type>integer</type>)
+       <indexterm>
+        <primary><varname>idle_in_transaction_timeout</> configuration parameter</primary>
+       </indexterm>
+       </term>
+       <listitem>
+        <para>
+        Abort any transaction that has been idle for longer than the specified
+        duration in seconds. This allows any locks the transaction may have taken
+        to be released.
+        </para>
+        <para>
+        A value of zero (the default) turns this off.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
       <varlistentry id="guc-vacuum-freeze-table-age" xreflabel="vacuum_freeze_table_age">
        <term><varname>vacuum_freeze_table_age</varname> (<type>integer</type>)
        <indexterm>
*** a/doc/src/sgml/mvcc.sgml
--- b/doc/src/sgml/mvcc.sgml
***************
*** 676,682 **** ERROR:  could not serialize access due to read/write dependencies among transact
       <listitem>
        <para>
         Don't leave connections dangling <quote>idle in transaction</quote>
!        longer than necessary.
        </para>
       </listitem>
       <listitem>
--- 676,684 ----
       <listitem>
        <para>
         Don't leave connections dangling <quote>idle in transaction</quote>
!        longer than necessary.  The configuration parameter
!        <xref linkend="guc-idle-in-transaction-timeout"> may be used to
!        automatically abort any such transactions.
        </para>
       </listitem>
       <listitem>
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 57,62 ****
--- 57,63 ----
  int			DeadlockTimeout = 1000;
  int			StatementTimeout = 0;
  int			LockTimeout = 0;
+ int			IdleInTransactionTimeout = 0;
  bool		log_lock_waits = false;
  
  /* Pointer to this process's PGPROC and PGXACT structs, if any */
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 3947,3952 **** PostgresMain(int argc, char *argv[],
--- 3947,3956 ----
  			{
  				set_ps_display("idle in transaction", false);
  				pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL);
+ 
+ 				/* Start the idle-in-transaction timer, in seconds */
+ 				if (IdleInTransactionTimeout > 0)
+ 					enable_timeout_after(IDLE_IN_TRANSACTION_TIMEOUT, 1000*IdleInTransactionTimeout);
  			}
  			else
  			{
***************
*** 3980,3986 **** PostgresMain(int argc, char *argv[],
  		DoingCommandRead = false;
  
  		/*
! 		 * (5) check for any other interesting events that happened while we
  		 * slept.
  		 */
  		if (got_SIGHUP)
--- 3984,3996 ----
  		DoingCommandRead = false;
  
  		/*
! 		 * (5) turn off the idle-in-transaction timeout
! 		 */
! 		if (IdleInTransactionTimeout > 0)
! 				disable_timeout(IDLE_IN_TRANSACTION_TIMEOUT, false);
! 
! 		/*
! 		 * (6) check for any other interesting events that happened while we
  		 * slept.
  		 */
  		if (got_SIGHUP)
***************
*** 3990,3996 **** PostgresMain(int argc, char *argv[],
  		}
  
  		/*
! 		 * (6) process the command.  But ignore it if we're skipping till
  		 * Sync.
  		 */
  		if (ignore_till_sync && firstchar != EOF)
--- 4000,4006 ----
  		}
  
  		/*
! 		 * (7) process the command.  But ignore it if we're skipping till
  		 * Sync.
  		 */
  		if (ignore_till_sync && firstchar != EOF)
*** a/src/backend/utils/errcodes.txt
--- b/src/backend/utils/errcodes.txt
***************
*** 227,232 **** Section: Class 25 - Invalid Transaction State
--- 227,233 ----
  25007    E    ERRCODE_SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED         schema_and_data_statement_mixing_not_supported
  25P01    E    ERRCODE_NO_ACTIVE_SQL_TRANSACTION                              no_active_sql_transaction
  25P02    E    ERRCODE_IN_FAILED_SQL_TRANSACTION                              in_failed_sql_transaction
+ 25P03    E    ERRCODE_IDLE_IN_TRANSACTION_TIMEOUT                            idle_in_transaction_timeout
  
  Section: Class 26 - Invalid SQL Statement Name
  
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 68,73 **** static void InitCommunication(void);
--- 68,74 ----
  static void ShutdownPostgres(int code, Datum arg);
  static void StatementTimeoutHandler(void);
  static void LockTimeoutHandler(void);
+ static void IdleInTransactionTimeoutHandler(void);
  static bool ThereIsAtLeastOneRole(void);
  static void process_startup_options(Port *port, bool am_superuser);
  static void process_settings(Oid databaseid, Oid roleid);
***************
*** 550,555 **** InitPostgres(const char *in_dbname, Oid dboid, const char *username,
--- 551,557 ----
  		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock);
  		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
  		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+ 		RegisterTimeout(IDLE_IN_TRANSACTION_TIMEOUT, IdleInTransactionTimeoutHandler);
  	}
  
  	/*
***************
*** 1092,1097 **** LockTimeoutHandler(void)
--- 1094,1106 ----
  	kill(MyProcPid, SIGINT);
  }
  
+ static void
+ IdleInTransactionTimeoutHandler(void)
+ {
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_IDLE_IN_TRANSACTION_TIMEOUT),
+ 			 errmsg("current transaction is aborted due to idle-in-transaction timeout")));
+ }
  
  /*
   * Returns true if at least one role is defined in this database cluster.
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1953,1958 **** static struct config_int ConfigureNamesInt[] =
--- 1953,1969 ----
  	},
  
  	{
+ 		{"idle_in_transaction_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
+ 			gettext_noop("A value of 0 turns off the timeout."),
+ 			GUC_UNIT_S
+ 		},
+ 		&IdleInTransactionTimeout,
+ 		0, 0, INT_MAX,
+ 		NULL, NULL, NULL
+ 	},
+ 
+ 	{
  		{"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
  			gettext_noop("Minimum age at which VACUUM should freeze a table row."),
  			NULL
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 509,514 ****
--- 509,515 ----
  #session_replication_role = 'origin'
  #statement_timeout = 0			# in milliseconds, 0 is disabled
  #lock_timeout = 0			# in milliseconds, 0 is disabled
+ #idle_in_transaction_timeout = 0			# in seconds, 0 is disabled
  #vacuum_freeze_min_age = 50000000
  #vacuum_freeze_table_age = 150000000
  #vacuum_multixact_freeze_min_age = 5000000
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
***************
*** 226,231 **** extern PGPROC *PreparedXactProcs;
--- 226,232 ----
  extern int	DeadlockTimeout;
  extern int	StatementTimeout;
  extern int	LockTimeout;
+ extern int	IdleInTransactionTimeout;
  extern bool log_lock_waits;
  
  
*** a/src/include/utils/timeout.h
--- b/src/include/utils/timeout.h
***************
*** 29,34 **** typedef enum TimeoutId
--- 29,35 ----
  	STATEMENT_TIMEOUT,
  	STANDBY_DEADLOCK_TIMEOUT,
  	STANDBY_TIMEOUT,
+ 	IDLE_IN_TRANSACTION_TIMEOUT,
  	/* First user-definable timeout reason */
  	USER_TIMEOUT,
  	/* Maximum number of timeout reasons */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to