]> granicus.if.org Git - postgresql/commitdiff
Add idle_in_transaction_session_timeout.
authorRobert Haas <rhaas@postgresql.org>
Wed, 16 Mar 2016 15:30:45 +0000 (11:30 -0400)
committerRobert Haas <rhaas@postgresql.org>
Wed, 16 Mar 2016 15:30:45 +0000 (11:30 -0400)
Vik Fearing, reviewed by Stéphane Schildknecht and me, and revised
slightly by me.

12 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/mvcc.sgml
src/backend/storage/lmgr/proc.c
src/backend/tcop/postgres.c
src/backend/utils/errcodes.txt
src/backend/utils/init/globals.c
src/backend/utils/init/postinit.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/miscadmin.h
src/include/storage/proc.h
src/include/utils/timeout.h

index 6c73fb439cc4ea6e23e841926adaa6652665c821..da971f5fca673a083f9ec62155df015e5c5499c0 100644 (file)
@@ -6063,6 +6063,26 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-in-transaction-session-timeout" xreflabel="idle_in_transaction_session_timeout">
+      <term><varname>idle_in_transaction_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_in_transaction_session_timeout</> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session with an open transaction that has been idle for
+       longer than the specified duration in milliseconds. This allows any
+       locks held by that session to be released and the connection slot to be reused;
+       it also allows tuples visible only to this transaction to be vacuumed.  See
+       <xref linkend="routine-vacuuming"> for more details about this.
+       </para>
+       <para>
+       The default value of 0 disables this feature.
+       </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>
index dd3c7755309733e5b610c418b5e8de44d78fc816..6352e12ee1eb5c74fa26a8a169db10fe078674a9 100644 (file)
@@ -725,7 +725,9 @@ 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.
+       longer than necessary.  The configuration parameter
+       <xref linkend="guc-idle-in-transaction-session-timeout"> may be used to
+       automatically disconnect lingering sessions.
       </para>
      </listitem>
      <listitem>
index 74ef4197986347c4975063390669d636e0a4975e..a66e07b7665ef342034904c1829ac75d1f6a9b16 100644 (file)
@@ -58,6 +58,7 @@
 int                    DeadlockTimeout = 1000;
 int                    StatementTimeout = 0;
 int                    LockTimeout = 0;
+int                    IdleInTransactionSessionTimeout = 0;
 bool           log_lock_waits = false;
 
 /* Pointer to this process's PGPROC and PGXACT structs, if any */
index 115166b6f6adcd12e1f90bb3164da8b0c7b84149..68811f1f21721bd3c8c20fef131c3195f5d4bb72 100644 (file)
@@ -2978,6 +2978,18 @@ ProcessInterrupts(void)
                }
        }
 
+       if (IdleInTransactionSessionTimeoutPending)
+       {
+               /* Has the timeout setting changed since last we looked? */
+               if (IdleInTransactionSessionTimeout > 0)
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT),
+                                        errmsg("terminating connection due to idle-in-transaction timeout")));
+               else
+                       IdleInTransactionSessionTimeoutPending = false;
+
+       }
+
        if (ParallelMessagePending)
                HandleParallelMessages();
 }
@@ -3553,6 +3565,7 @@ PostgresMain(int argc, char *argv[],
        StringInfoData input_message;
        sigjmp_buf      local_sigjmp_buf;
        volatile bool send_ready_for_query = true;
+       bool            disable_idle_in_transaction_timeout = false;
 
        /* Initialize startup process environment if necessary. */
        if (!IsUnderPostmaster)
@@ -3942,11 +3955,27 @@ PostgresMain(int argc, char *argv[],
                        {
                                set_ps_display("idle in transaction (aborted)", false);
                                pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL);
+
+                               /* Start the idle-in-transaction timer */
+                               if (IdleInTransactionSessionTimeout > 0)
+                               {
+                                       disable_idle_in_transaction_timeout = true;
+                                       enable_timeout_after(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+                                                                                IdleInTransactionSessionTimeout);
+                               }
                        }
                        else if (IsTransactionOrTransactionBlock())
                        {
                                set_ps_display("idle in transaction", false);
                                pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL);
+
+                               /* Start the idle-in-transaction timer */
+                               if (IdleInTransactionSessionTimeout > 0)
+                               {
+                                       disable_idle_in_transaction_timeout = true;
+                                       enable_timeout_after(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+                                                                                IdleInTransactionSessionTimeout);
+                               }
                        }
                        else
                        {
@@ -3987,7 +4016,16 @@ PostgresMain(int argc, char *argv[],
                DoingCommandRead = false;
 
                /*
-                * (5) check for any other interesting events that happened while we
+                * (5) turn off the idle-in-transaction timeout
+                */
+               if (disable_idle_in_transaction_timeout)
+               {
+                       disable_timeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT, false);
+                       disable_idle_in_transaction_timeout = false;
+               }
+
+               /*
+                * (6) check for any other interesting events that happened while we
                 * slept.
                 */
                if (got_SIGHUP)
@@ -3997,7 +4035,7 @@ PostgresMain(int argc, char *argv[],
                }
 
                /*
-                * (6) process the command.  But ignore it if we're skipping till
+                * (7) process the command.  But ignore it if we're skipping till
                 * Sync.
                 */
                if (ignore_till_sync && firstchar != EOF)
index 04c9c00a27b02100ce5e590919ee27edff762f0b..1a920e8bd2114df05a204f2c551d7e37c658a2c1 100644 (file)
@@ -229,6 +229,7 @@ Section: Class 25 - Invalid Transaction State
 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_SESSION_TIMEOUT                    idle_in_transaction_session_timeout
 
 Section: Class 26 - Invalid SQL Statement Name
 
index ccd9c8e7f8724b1386d60fa70b08297a99da9260..597dab430143369d4b61eded6977a4c875cc1e1b 100644 (file)
@@ -30,6 +30,7 @@ volatile bool InterruptPending = false;
 volatile bool QueryCancelPending = false;
 volatile bool ProcDiePending = false;
 volatile bool ClientConnectionLost = false;
+volatile bool IdleInTransactionSessionTimeoutPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
 volatile uint32 QueryCancelHoldoffCount = 0;
 volatile uint32 CritSectionCount = 0;
index 6b760d4265b5e6085dea0b839c13f7e5914ada00..b3f1bc46d977d98cad9d68bef3850a8a7ff78107 100644 (file)
@@ -70,6 +70,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
 static void process_settings(Oid databaseid, Oid roleid);
@@ -597,6 +598,8 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
                RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
                RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
                RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+               RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+                                               IdleInTransactionSessionTimeoutHandler);
        }
 
        /*
@@ -1178,6 +1181,13 @@ LockTimeoutHandler(void)
        kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleInTransactionSessionTimeoutHandler(void)
+{
+       IdleInTransactionSessionTimeoutPending = true;
+       InterruptPending = true;
+       SetLatch(MyLatch);
+}
 
 /*
  * Returns true if at least one role is defined in this database cluster.
index f0d4ec165e9a6b5ada7d4cfda317977aa7ea01bd..be9d5cacc07e9a26d1c33fb108ec860747edcd7a 100644 (file)
@@ -2065,6 +2065,17 @@ static struct config_int ConfigureNamesInt[] =
                NULL, NULL, NULL
        },
 
+       {
+               {"idle_in_transaction_session_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_MS
+               },
+               &IdleInTransactionSessionTimeout,
+               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."),
index ee3d37863ec35216375156335fb6da4aa4e32ef7..a6bb33557cb4722fcdfdd305e77599d5e1f2791a 100644 (file)
 #session_replication_role = 'origin'
 #statement_timeout = 0                 # in milliseconds, 0 is disabled
 #lock_timeout = 0                      # in milliseconds, 0 is disabled
+#idle_in_transaction_session_timeout = 0               # in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
index cc7833e6cda9eb72e70c66f0786b74923911b22c..9200f045a24b6e33aea35935be0b5e5924d3eca8 100644 (file)
@@ -80,6 +80,7 @@
 extern PGDLLIMPORT volatile bool InterruptPending;
 extern PGDLLIMPORT volatile bool QueryCancelPending;
 extern PGDLLIMPORT volatile bool ProcDiePending;
+extern PGDLLIMPORT volatile bool IdleInTransactionSessionTimeoutPending;
 
 extern volatile bool ClientConnectionLost;
 
index 612fa052be01370a2fa41da62003d406731ebcab..c3b462c94946927674483e0d5972b73b5554fdea 100644 (file)
@@ -257,6 +257,7 @@ extern PGPROC *PreparedXactProcs;
 extern int     DeadlockTimeout;
 extern int     StatementTimeout;
 extern int     LockTimeout;
+extern int     IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
 
index 14e9720c8856854b1d8825eb88308ef834d32b72..f64921e2d676fa62713e1353235cbed9113eadf7 100644 (file)
@@ -30,6 +30,7 @@ typedef enum TimeoutId
        STANDBY_DEADLOCK_TIMEOUT,
        STANDBY_TIMEOUT,
        STANDBY_LOCK_TIMEOUT,
+       IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
        /* First user-definable timeout reason */
        USER_TIMEOUT,
        /* Maximum number of timeout reasons */