Terminate the idle sessions

Started by Li Japinover 5 years ago52 messages
#1Li Japin
japinli@hotmail.com
1 attachment(s)

Hi, hackers

When some clients connect to database in idle state, postgres do not close the idle sessions,
here i add a new GUC idle_session_timeout to let postgres close the idle sessions, it samilar
to idle_in_transaction_session_timeout.

Best, regards.

Japin Li

Attachments:

0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=0001-Allow-terminating-the-idle-sessions.patchDownload
From 55793e3498017f9a72fdd99798fecbb1e4f3a96c Mon Sep 17 00:00:00 2001
From: Japin <jianping.li@ww-it.cn>
Date: Tue, 9 Jun 2020 16:48:57 +0800
Subject: [PATCH] Allow terminating the idle sessions

---
 doc/src/sgml/config.sgml          | 17 +++++++++++++++++
 src/backend/storage/lmgr/proc.c   |  1 +
 src/backend/tcop/postgres.c       | 25 +++++++++++++++++++++++++
 src/backend/utils/errcodes.txt    |  1 +
 src/backend/utils/init/globals.c  |  1 +
 src/backend/utils/init/postinit.c | 10 ++++++++++
 src/backend/utils/misc/guc.c      | 11 +++++++++++
 src/include/miscadmin.h           |  1 +
 src/include/storage/proc.h        |  1 +
 src/include/utils/timeout.h       |  1 +
 10 files changed, 69 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index aca8f73a..95a948d9 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8110,6 +8110,23 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+       If this value is specified without units, it is taken as milliseconds.
+       A value of zero (the default) disables the timeout.
+       </para>
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index f5eef6fa..85f7052e 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c9424f16..15c21bc4 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3783,6 +3793,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4237,6 +4248,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4277,6 +4296,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed..d5935a2c 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index eb196444..eb863b29 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f4247ea7..66445457 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -72,6 +72,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -629,6 +630,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1233,6 +1235,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2f3e0a70..8375ccb7 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2516,6 +2516,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 18bc8a7b..82606aca 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1ee9000b..33e8c354 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -293,6 +293,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f67..6be398c9 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -30,6 +30,7 @@ typedef enum TimeoutId
 	STANDBY_DEADLOCK_TIMEOUT,
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
-- 
2.26.2

#2David G. Johnston
david.g.johnston@gmail.com
In reply to: Li Japin (#1)
Re: Terminate the idle sessions

On Tuesday, June 9, 2020, Li Japin <japinli@hotmail.com> wrote:

Hi, hackers

When some clients connect to database in idle state, postgres do not close
the idle sessions,
here i add a new GUC idle_session_timeout to let postgres close the idle
sessions, it samilar
to idle_in_transaction_session_timeout

I’m curious as to the use case because I cannot imagine using this. Idle
connections are normal. Seems better to monitor them and conditionally
execute the disconnect backend function from the monitoring layer than
indiscriminately disconnect based upon time. Though i do see an
interesting case for attaching to specific login user accounts that only
manually login and want the equivalent of a timed screen lock.

David J.

#3Li Japin
japinli@hotmail.com
In reply to: David G. Johnston (#2)
Re: Terminate the idle sessions

On Jun 9, 2020, at 10:35 PM, David G. Johnston <david.g.johnston@gmail.com<mailto:david.g.johnston@gmail.com>> wrote:

I’m curious as to the use case because I cannot imagine using this. Idle connections are normal. Seems better to monitor them and conditionally execute the disconnect backend function from the monitoring layer than indiscriminately disconnect based upon time.

I agree with you. But we can also give the user to control the idle sessions lifetime.

#4Michael Paquier
michael@paquier.xyz
In reply to: Li Japin (#3)
Re: Terminate the idle sessions

On Wed, Jun 10, 2020 at 05:20:36AM +0000, Li Japin wrote:

I agree with you. But we can also give the user to control the idle
sessions lifetime.

Idle sessions staying around can be a problem in the long run as they
impact snapshot building. You could for example use a background
worker to do this work, like that:
https://github.com/michaelpq/pg_plugins/tree/master/kill_idle
--
Michael

#5Li Japin
japinli@hotmail.com
In reply to: Michael Paquier (#4)
Re: Terminate the idle sessions

On Jun 10, 2020, at 4:25 PM, Michael Paquier <michael@paquier.xyz<mailto:michael@paquier.xyz>> wrote:

Idle sessions staying around can be a problem in the long run as they
impact snapshot building. You could for example use a background
worker to do this work, like that:
https://github.com/michaelpq/pg_plugins/tree/master/kill_idle

Why not implement it in the core of Postgres? Are there any disadvantages of
implementing it in the core of Postgres?

Japin Li

#6Adam Brusselback
adambrusselback@gmail.com
In reply to: Li Japin (#5)
Re: Terminate the idle sessions

Why not implement it in the core of Postgres? Are there any

disadvantages of

implementing it in the core of Postgres?

I was surprised this wasn't a feature when I looked into it a couple years
ago. I'd use it if it were built in, but I am not installing something
extra just for this.

I’m curious as to the use case because I cannot imagine using this.

My use case is, I have a primary application that connects to the DB, most
users work through that (setting is useless for this scenario, app manages
it's connections well enough). I also have a number of internal users who
deal with data ingestion and connect to the DB directly to work, and those
users sometimes leave query windows open for days accidentally. Generally
not an issue, but would be nice to be able to time those connections out.

Just my $0.02, but I am +1.
-Adam

#7Li Japin
japinli@hotmail.com
In reply to: Adam Brusselback (#6)
Re: Terminate the idle sessions

On Jun 10, 2020, at 10:27 PM, Adam Brusselback <adambrusselback@gmail.com<mailto:adambrusselback@gmail.com>> wrote:

My use case is, I have a primary application that connects to the DB, most users work through that (setting is useless for this scenario, app manages it's connections well enough). I also have a number of internal users who deal with data ingestion and connect to the DB directly to work, and those users sometimes leave query windows open for days accidentally. Generally not an issue, but would be nice to be able to time those connections out.

If there is no big impact, I think we might add it builtin.

Japin Li

#8Cary Huang
cary.huang@highgo.ca
In reply to: Li Japin (#7)
Re: Terminate the idle sessions

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: tested, passed
Documentation: tested, passed

I applied this patch to the PG13 branch and generally this feature works as described. The new "idle_session_timeout" that controls the idle session disconnection is not in the default postgresql.conf and I think it should be included there with default value 0, which means disabled.
There is currently no enforced minimum value for "idle_session_timeout" (except for value 0 for disabling the feature), so user can put any value larger than 0 and it could be very small like 500 or even 50 millisecond, this would make any psql connection to disconnect shortly after it has connected, which may not be ideal. Many systems I have worked with have 30 minutes inactivity timeout by default, and I think it would be better and safer to enforce a reasonable minimum timeout value

#9David G. Johnston
david.g.johnston@gmail.com
In reply to: Cary Huang (#8)
Re: Terminate the idle sessions

On Mon, Aug 10, 2020 at 2:43 PM Cary Huang <cary.huang@highgo.ca> wrote:

There is currently no enforced minimum value for "idle_session_timeout"
(except for value 0 for disabling the feature), so user can put any value
larger than 0 and it could be very small like 500 or even 50 millisecond,
this would make any psql connection to disconnect shortly after it has
connected, which may not be ideal. Many systems I have worked with have 30
minutes inactivity timeout by default, and I think it would be better and
safer to enforce a reasonable minimum timeout value

I'd accept a value of say 1,000 being minimum in order to reinforce the
fact that a unit-less input, while possible, is taken to be milliseconds
and such small values most likely mean the user has made a mistake. I
would not choose a minimum allowed value solely based on our concept of
"reasonable". I don't imagine a value of say 10 seconds, while seemingly
unreasonable, is going to be unsafe.

David J.

#10Li Japin
japinli@hotmail.com
In reply to: Cary Huang (#8)
1 attachment(s)
Re: Terminate the idle sessions

Hi,

On Aug 11, 2020, at 5:42 AM, Cary Huang <cary.huang@highgo.ca<mailto:cary.huang@highgo.ca>> wrote:

I applied this patch to the PG13 branch and generally this feature works as described. The new "idle_session_timeout" that controls the idle session disconnection is not in the default postgresql.conf and I think it should be included there with default value 0, which means disabled.

Thanks for looking at it!

I’ve attached a new version that add “idle_session_timeout” in the default postgresql.conf.

On Mon, Aug 10, 2020 at 2:43 PM Cary Huang <cary.huang@highgo.ca<mailto:cary.huang@highgo.ca>> wrote:
There is currently no enforced minimum value for "idle_session_timeout" (except for value 0 for disabling the feature), so user can put any value larger than 0 and it could be very small like 500 or even 50 millisecond, this would make any psql connection to disconnect shortly after it has connected, which may not be ideal. Many systems I have worked with have 30 minutes inactivity timeout by default, and I think it would be better and safer to enforce a reasonable minimum timeout value

I'd accept a value of say 1,000 being minimum in order to reinforce the fact that a unit-less input, while possible, is taken to be milliseconds and such small values most likely mean the user has made a mistake. I would not choose a minimum allowed value solely based on our concept of "reasonable". I don't imagine a value of say 10 seconds, while seemingly unreasonable, is going to be unsafe.

I think David is right, see “idle_in_transaction_session_timeout”, it also doesn’t have a “reasonable” minimum value.

Attachments:

v2-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v2-0001-Allow-terminating-the-idle-sessions.patchDownload
From 9c18608276164868bdcc5579cf2a3cc18584ae8d Mon Sep 17 00:00:00 2001
From: Japin <jianping.li@ww-it.cn>
Date: Tue, 11 Aug 2020 10:52:14 +0800
Subject: [PATCH] Allow terminating the idle sessions

---
 doc/src/sgml/config.sgml                      | 17 +++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 +++++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 ++++++++
 src/backend/utils/misc/guc.c                  | 11 ++++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 70 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7a7177c550..3a08fc3859 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8185,6 +8185,23 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+       If this value is specified without units, it is taken as milliseconds.
+       A value of zero (the default) disables the timeout.
+       </para>
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index e57fcd2538..1903ce9557 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c9424f167c..15c21bc4fe 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3783,6 +3793,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4237,6 +4248,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4277,6 +4296,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f4247ea70d..6644545727 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -72,6 +72,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -629,6 +630,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1233,6 +1235,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index de87ad6ef7..cd81177c4f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 5ceb2494ba..cb4f76d3f3 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -299,6 +299,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..6be398c983 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -30,6 +30,7 @@ typedef enum TimeoutId
 	STANDBY_DEADLOCK_TIMEOUT,
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
-- 
2.27.0

#11Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Li Japin (#10)
Re: Terminate the idle sessions

On Tue, Aug 11, 2020 at 8:45 AM Li Japin <japinli@hotmail.com> wrote:

I’ve attached a new version that add “idle_session_timeout” in the default postgresql.conf.

Hi, I would like to just mention a use case I thought of while discussing [1]/messages/by-id/CALj2ACU1NBQo9mihA15dFf6udkOi7m0u2_s5QJ6dzk=ZQyVbwQ@mail.gmail.com:

In postgres_fdw: assuming we use idle_in_session_timeout on remote
backends, the remote sessions will be closed after timeout, but the
locally cached connection cache entries still exist and become stale.
The subsequent queries that may use the cached connections will fail,
of course these subsequent queries can retry the connections only at
the beginning of a remote txn but not in the middle of a remote txn,
as being discussed in [2]/messages/by-id/CALj2ACUAi23vf1WiHNar_LksM9EDOWXcbHCo-fD4Mbr1d=78YQ@mail.gmail.com. For instance, in a long running local txn,
let say we used a remote connection at the beginning of the local
txn(note that it will open a remote session and it's entry is cached
in local connection cache), only we use the cached connection later at
some point in the local txn, by then let say the
idle_in_session_timeout has happened on the remote backend and the
remote session would have been closed, the long running local txn will
fail instead of succeeding.

I think, since the idle_session_timeout is by default disabled, we
have no problem. My thought is what if a user enables the
feature(knowingly or unknowingly) on the remote backend? If the user
knows about the above scenario, that may be fine. On the other hand,
either we can always the feature on the remote backend(at the
beginning of the remote txn, like we set for some other configuration
settings see - configure_remote_session() in connection.c) or how
about mentioning the above scenario in this feature documentation?

[1]: /messages/by-id/CALj2ACU1NBQo9mihA15dFf6udkOi7m0u2_s5QJ6dzk=ZQyVbwQ@mail.gmail.com
[2]: /messages/by-id/CALj2ACUAi23vf1WiHNar_LksM9EDOWXcbHCo-fD4Mbr1d=78YQ@mail.gmail.com

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#12Li Japin
japinli@hotmail.com
In reply to: Bharath Rupireddy (#11)
Re: Terminate the idle sessions

On Aug 14, 2020, at 2:15 PM, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com<mailto:bharath.rupireddyforpostgres@gmail.com>> wrote:

I think, since the idle_session_timeout is by default disabled, we
have no problem. My thought is what if a user enables the
feature(knowingly or unknowingly) on the remote backend? If the user
knows about the above scenario, that may be fine. On the other hand,
either we can always the feature on the remote backend(at the
beginning of the remote txn, like we set for some other configuration
settings see - configure_remote_session() in connection.c) or how
about mentioning the above scenario in this feature documentation?

Though we can disable the idle_session_timeout when using postgres_fdw,
there still has locally cached connection cache entries when the remote sessions
terminated by accident. AFAIK, you have provided a patch to solve this
problem, and it is in current CF [1]https://commitfest.postgresql.org/29/2651/.

[1]: https://commitfest.postgresql.org/29/2651/

Best Regards,
Japin Li.

#13Bharath Rupireddy
bharath.rupireddyforpostgres@gmail.com
In reply to: Li Japin (#12)
Re: Terminate the idle sessions

On Fri, Aug 14, 2020 at 1:32 PM Li Japin <japinli@hotmail.com> wrote:

On Aug 14, 2020, at 2:15 PM, Bharath Rupireddy <

bharath.rupireddyforpostgres@gmail.com> wrote:

I think, since the idle_session_timeout is by default disabled, we
have no problem. My thought is what if a user enables the
feature(knowingly or unknowingly) on the remote backend? If the user
knows about the above scenario, that may be fine. On the other hand,
either we can always the feature on the remote backend(at the
beginning of the remote txn, like we set for some other configuration
settings see - configure_remote_session() in connection.c) or how
about mentioning the above scenario in this feature documentation?

Though we can disable the idle_session_timeout when using postgres_fdw,
there still has locally cached connection cache entries when the remote

sessions

terminated by accident. AFAIK, you have provided a patch to solve this
problem, and it is in current CF [1].

[1] - https://commitfest.postgresql.org/29/2651/

Yes, that solution can retry the cached connections at only the beginning
of the remote txn and not at the middle of the remote txn and that makes
sense as we can not retry connecting to a different remote backend in the
middle of a remote txn.

+1 for disabling the idle_session_timeout feature in case of postgres_fdw.
This can avoid the remote backends to timeout during postgres_fdw usages.

With Regards,
Bharath Rupireddy.
EnterpriseDB: http://www.enterprisedb.com

#14Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Bharath Rupireddy (#13)
Re: Terminate the idle sessions

Hello.

At Mon, 17 Aug 2020 19:28:10 +0530, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com> wrote in

On Fri, Aug 14, 2020 at 1:32 PM Li Japin <japinli@hotmail.com> wrote:

On Aug 14, 2020, at 2:15 PM, Bharath Rupireddy <

bharath.rupireddyforpostgres@gmail.com> wrote:

I think, since the idle_session_timeout is by default disabled, we
have no problem. My thought is what if a user enables the
feature(knowingly or unknowingly) on the remote backend? If the user
knows about the above scenario, that may be fine. On the other hand,
either we can always the feature on the remote backend(at the
beginning of the remote txn, like we set for some other configuration
settings see - configure_remote_session() in connection.c) or how
about mentioning the above scenario in this feature documentation?

Though we can disable the idle_session_timeout when using postgres_fdw,
there still has locally cached connection cache entries when the remote

sessions

terminated by accident. AFAIK, you have provided a patch to solve this
problem, and it is in current CF [1].

[1] - https://commitfest.postgresql.org/29/2651/

Yes, that solution can retry the cached connections at only the beginning
of the remote txn and not at the middle of the remote txn and that makes
sense as we can not retry connecting to a different remote backend in the
middle of a remote txn.

+1 for disabling the idle_session_timeout feature in case of postgres_fdw.
This can avoid the remote backends to timeout during postgres_fdw usages.

The same already happens for idle_in_transaction_session_timeout and
we can use "ALTER ROLE/DATABASE SET" to dislable or loosen them, it's
a bit cumbersome, though. I don't think we should (at least
implicitly) disable those timeouts ad-hockerly for postgres_fdw.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#15Li Japin
japinli@hotmail.com
In reply to: Kyotaro Horiguchi (#14)
Re: Terminate the idle sessions

On Aug 18, 2020, at 9:19 AM, Kyotaro Horiguchi <horikyota.ntt@gmail.com<mailto:horikyota.ntt@gmail.com>> wrote:

The same already happens for idle_in_transaction_session_timeout and
we can use "ALTER ROLE/DATABASE SET" to dislable or loosen them, it's
a bit cumbersome, though. I don't think we should (at least
implicitly) disable those timeouts ad-hockerly for postgres_fdw.

+1.

#16Thomas Munro
thomas.munro@gmail.com
In reply to: Li Japin (#15)
1 attachment(s)
Re: Terminate the idle sessions

On Tue, Aug 18, 2020 at 2:13 PM Li Japin <japinli@hotmail.com> wrote:

On Aug 18, 2020, at 9:19 AM, Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote:
The same already happens for idle_in_transaction_session_timeout and
we can use "ALTER ROLE/DATABASE SET" to dislable or loosen them, it's
a bit cumbersome, though. I don't think we should (at least
implicitly) disable those timeouts ad-hockerly for postgres_fdw.

+1.

This seems like a reasonable feature to me.

The delivery of the error message explaining what happened is probably
not reliable, so to some clients and on some operating systems this
will be indistinguishable from a dropped network connection or other
error, but that's OK and we already have that problem with the
existing timeout-based disconnection feature.

The main problem I have with it is the high frequency setitimer()
calls. If you enable both statement_timeout and idle_session_timeout,
then we get up to huge number of system calls, like the following
strace -c output for a few seconds of one backend under pgbench -S
workload shows:

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
39.45 0.118685 0 250523 setitimer
29.98 0.090200 0 125275 sendto
24.30 0.073107 0 126235 973 recvfrom
6.01 0.018068 0 20950 pread64
0.26 0.000779 0 973 epoll_wait
------ ----------- ----------- --------- --------- ----------------
100.00 0.300839 523956 973 total

There's a small but measurable performance drop from this, as also
discussed in another thread about another kind of timeout[1]/messages/by-id/77def86b27e41f0efcba411460e929ae@postgrespro.ru. Maybe
we should try to fix that with something like the attached?

[1]: /messages/by-id/77def86b27e41f0efcba411460e929ae@postgrespro.ru

Attachments:

0001-Optimize-setitimer-usage.patchxapplication/octet-stream; name=0001-Optimize-setitimer-usage.patchxDownload
From af1e73116f105cb3d2cdc4da2f9289c30707fc39 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 42 ++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..b5027815db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -591,8 +610,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +621,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.20.1

#17Li Japin
japinli@hotmail.com
In reply to: Thomas Munro (#16)
Re: Terminate the idle sessions

On Aug 31, 2020, at 8:51 AM, Thomas Munro <thomas.munro@gmail.com<mailto:thomas.munro@gmail.com>> wrote:

The main problem I have with it is the high frequency setitimer()
calls. If you enable both statement_timeout and idle_session_timeout,
then we get up to huge number of system calls, like the following
strace -c output for a few seconds of one backend under pgbench -S
workload shows:

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
39.45 0.118685 0 250523 setitimer
29.98 0.090200 0 125275 sendto
24.30 0.073107 0 126235 973 recvfrom
6.01 0.018068 0 20950 pread64
0.26 0.000779 0 973 epoll_wait
------ ----------- ----------- --------- --------- ----------------
100.00 0.300839 523956 973 total

Hi, Thomas,

Could you give the more details about the test instructions?

#18Thomas Munro
thomas.munro@gmail.com
In reply to: Li Japin (#17)
Re: Terminate the idle sessions

On Mon, Aug 31, 2020 at 2:40 PM Li Japin <japinli@hotmail.com> wrote:

Could you give the more details about the test instructions?

Hi Japin,

Sure. Because I wasn't trying to get reliable TPS number or anything,
I just used a simple short read-only test with one connection, like
this:

pgbench -i -s10 postgres
pgbench -T60 -Mprepared -S postgres

Then I looked for the active backend and ran strace -c -p XXX for a
few seconds and hit ^C to get the counters. I doubt the times are
very accurate, but the number of calls is informative.

If you do that on a server running with -c statement_timeout=10s, you
see one setitimer() per transaction. If you also use -c
idle_session_timeout=10s at the same time, you see two.

#19Kyotaro Horiguchi
horikyota.ntt@gmail.com
In reply to: Thomas Munro (#16)
Re: Terminate the idle sessions

At Mon, 31 Aug 2020 12:51:20 +1200, Thomas Munro <thomas.munro@gmail.com> wrote in

On Tue, Aug 18, 2020 at 2:13 PM Li Japin <japinli@hotmail.com> wrote:

On Aug 18, 2020, at 9:19 AM, Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote:
The same already happens for idle_in_transaction_session_timeout and
we can use "ALTER ROLE/DATABASE SET" to dislable or loosen them, it's
a bit cumbersome, though. I don't think we should (at least
implicitly) disable those timeouts ad-hockerly for postgres_fdw.

+1.

This seems like a reasonable feature to me.

The delivery of the error message explaining what happened is probably
not reliable, so to some clients and on some operating systems this
will be indistinguishable from a dropped network connection or other
error, but that's OK and we already have that problem with the
existing timeout-based disconnection feature.

The main problem I have with it is the high frequency setitimer()
calls. If you enable both statement_timeout and idle_session_timeout,
then we get up to huge number of system calls, like the following
strace -c output for a few seconds of one backend under pgbench -S
workload shows:

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
39.45 0.118685 0 250523 setitimer
29.98 0.090200 0 125275 sendto
24.30 0.073107 0 126235 973 recvfrom
6.01 0.018068 0 20950 pread64
0.26 0.000779 0 973 epoll_wait
------ ----------- ----------- --------- --------- ----------------
100.00 0.300839 523956 973 total

There's a small but measurable performance drop from this, as also
discussed in another thread about another kind of timeout[1]. Maybe
we should try to fix that with something like the attached?

[1] /messages/by-id/77def86b27e41f0efcba411460e929ae@postgrespro.ru

I think it's worth doing. Maybe we can get rid of doing anything other
than removing an entry in the case where we disable a non-nearest
timeout.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

#20Li Japin
japinli@hotmail.com
In reply to: Thomas Munro (#18)
2 attachment(s)
Re: Terminate the idle sessions

On Aug 31, 2020, at 11:43 AM, Thomas Munro <thomas.munro@gmail.com> wrote:

On Mon, Aug 31, 2020 at 2:40 PM Li Japin <japinli@hotmail.com> wrote:

Could you give the more details about the test instructions?

Hi Japin,

Sure. Because I wasn't trying to get reliable TPS number or anything,
I just used a simple short read-only test with one connection, like
this:

pgbench -i -s10 postgres
pgbench -T60 -Mprepared -S postgres

Then I looked for the active backend and ran strace -c -p XXX for a
few seconds and hit ^C to get the counters. I doubt the times are
very accurate, but the number of calls is informative.

If you do that on a server running with -c statement_timeout=10s, you
see one setitimer() per transaction. If you also use -c
idle_session_timeout=10s at the same time, you see two.

Hi, Thomas,

Thanks for your point out this problem, here is the comparison.

Without Optimize settimer usage:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
41.22 1.444851 1 1317033 setitimer
28.41 0.995936 2 658622 sendto
24.63 0.863316 1 659116 599 recvfrom
5.71 0.200275 2 111055 pread64
0.03 0.001152 2 599 epoll_wait
0.00 0.000000 0 1 epoll_ctl
------ ----------- ----------- --------- --------- ----------------
100.00 3.505530 2746426 599 total

With Optimize settimer usage:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
49.89 1.464332 1 1091429 sendto
40.83 1.198389 1 1091539 219 recvfrom
9.26 0.271890 1 183321 pread64
0.02 0.000482 2 214 epoll_wait
0.00 0.000013 3 5 setitimer
0.00 0.000010 2 5 rt_sigreturn
0.00 0.000000 0 1 epoll_ctl
------ ----------- ----------- --------- --------- ----------------
100.00 2.935116 2366514 219 total

Here’s a modified version of Thomas’s patch.

Attachments:

v3-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v3-0001-Allow-terminating-the-idle-sessions.patchDownload
From 4487dd40a67af5338fa60708f6975b3da1588ba7 Mon Sep 17 00:00:00 2001
From: Japin <jianping.li@ww-it.cn>
Date: Tue, 11 Aug 2020 10:52:14 +0800
Subject: [PATCH v3 1/2] Allow terminating the idle sessions

---
 doc/src/sgml/config.sgml                      | 17 +++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 +++++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 ++++++++
 src/backend/utils/misc/guc.c                  | 11 ++++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 70 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 7a7177c550..3a08fc3859 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8185,6 +8185,23 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+       If this value is specified without units, it is taken as milliseconds.
+       A value of zero (the default) disables the timeout.
+       </para>
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index aa9fbd8054..7fe3ca1a39 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index c9424f167c..15c21bc4fe 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3783,6 +3793,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4237,6 +4248,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4277,6 +4296,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index d4ab4c7e23..5c88c4cfff 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -72,6 +72,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -629,6 +630,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1237,6 +1239,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index de87ad6ef7..cd81177c4f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 9c9a50ae45..a0ccabe3ec 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -368,6 +368,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..6be398c983 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -30,6 +30,7 @@ typedef enum TimeoutId
 	STANDBY_DEADLOCK_TIMEOUT,
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
-- 
2.28.0

v3-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v3-0002-Optimize-setitimer-usage.patchDownload
From 198d61f58ba08fe52f94bc985ea4602a77712ca0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v3 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 42 ++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..b5027815db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -591,8 +610,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +621,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.28.0

#21kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#20)
RE: Terminate the idle sessions

Dear Li,

I read your patch, and I think the documentation is too simple to avoid all problems.
(I think if some connection pooling is used, the same problem will occur.)
Could you add some explanations in the doc file? I made an example:

```
Note that this values should be set to zero if you use postgres_fdw or some
Connection-pooling software, because connections might be closed unexpectedly.
```

I will send other comments if I find something.

Hayato Kuroda
FUJITSU LIMITED

#22Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#21)
2 attachment(s)
Re: Terminate the idle sessions

On Nov 13, 2020, at 6:27 PM, kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> wrote:

I read your patch, and I think the documentation is too simple to avoid all problems.
(I think if some connection pooling is used, the same problem will occur.)
Could you add some explanations in the doc file? I made an example:

```
Note that this values should be set to zero if you use postgres_fdw or some
Connection-pooling software, because connections might be closed unexpectedly.
```

Thanks for your advice! Attached v4.

--
Best regards
Japin Li

Attachments:

v4-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v4-0001-Allow-terminating-the-idle-sessions.patchDownload
From e3a29af407d03451c76b25b4a86f3fb4ad2e028d Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v4 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
postgres_fdw or some connection-pooling software, because connections
might be closed unexpectedly.
---
 doc/src/sgml/config.sgml                      | 25 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 +++++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 ++++++++
 src/backend/utils/misc/guc.c                  | 11 ++++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 78 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f043433e31..6c4e2a1fdc 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8273,6 +8273,31 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+       If this value is specified without units, it is taken as milliseconds.
+       A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use postgres_fdw or some
+         connection-pooling software, because connections might be closed unexpectedly.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 88566bd9fa..38ada73279 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..313900e998 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 9c9a50ae45..a0ccabe3ec 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -368,6 +368,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..6be398c983 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -30,6 +30,7 @@ typedef enum TimeoutId
 	STANDBY_DEADLOCK_TIMEOUT,
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
-- 
2.20.1 (Apple Git-117)

v4-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v4-0002-Optimize-setitimer-usage.patchDownload
From 99b70bbc4e326fe7c545a295a026b81f7ac2a074 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v4 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 42 ++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..b5027815db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -591,8 +610,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +621,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.20.1 (Apple Git-117)

#23kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#22)
RE: Terminate the idle sessions

Dear Li,

Thanks for your advice! Attached v4.

I confirmed it. OK.

@@ -30,6 +30,7 @@ typedef enum TimeoutId
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
STANDBY_LOCK_TIMEOUT,
+ IDLE_SESSION_TIMEOUT,
IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
/* First user-definable timeout reason */
USER_TIMEOUT,

I'm not familiar with timeout, but I can see that the priority of idle-session is set lower than transaction-timeout.
Could you explain the reason? In my image this timeout locates at the lowest layer, so it might have the lowest
priority.

Other codes are still checked :-(.

Hayato Kuroda
FUJITSU LIMITED

#24Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#23)
2 attachment(s)
Re: Terminate the idle sessions

Hi Kuroda,

On Nov 16, 2020, at 1:22 PM, kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> wrote:

@@ -30,6 +30,7 @@ typedef enum TimeoutId
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
STANDBY_LOCK_TIMEOUT,
+ IDLE_SESSION_TIMEOUT,
IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
/* First user-definable timeout reason */
USER_TIMEOUT,

I'm not familiar with timeout, but I can see that the priority of idle-session is set lower than transaction-timeout.
Could you explain the reason? In my image this timeout locates at the lowest layer, so it might have the lowest
priority.

My apologies! I just add a enum for idle session and ignore the comments that says the enum has priority.
Fixed as follows:

@@ -30,8 +30,8 @@ typedef enum TimeoutId
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
STANDBY_LOCK_TIMEOUT,
- IDLE_SESSION_TIMEOUT,
IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+ IDLE_SESSION_TIMEOUT,
/* First user-definable timeout reason */
USER_TIMEOUT,
/* Maximum number of timeout reasons */

Thanks for your review! Attached.

--
Best regards
Japin Li

Attachments:

v5-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v5-0001-Allow-terminating-the-idle-sessions.patchDownload
From e179ae49562bc8c70b179e84b242b101135bb0ad Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v5 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
postgres_fdw or some connection-pooling software, because connections
might be closed unexpectedly.
---
 doc/src/sgml/config.sgml                      | 25 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 +++++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 ++++++++
 src/backend/utils/misc/guc.c                  | 11 ++++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 78 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f043433e31..6c4e2a1fdc 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8273,6 +8273,31 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+       Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+       If this value is specified without units, it is taken as milliseconds.
+       A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use postgres_fdw or some
+         connection-pooling software, because connections might be closed unexpectedly.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 88566bd9fa..38ada73279 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..313900e998 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 9c9a50ae45..a0ccabe3ec 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -368,6 +368,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..15eeb0ed2c 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -31,6 +31,7 @@ typedef enum TimeoutId
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
-- 
2.28.0

v5-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v5-0002-Optimize-setitimer-usage.patchDownload
From aa866f7589c95a2d7fcff0c9bbcc9673145d9ccf Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v5 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 42 ++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..b5027815db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -591,8 +610,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +621,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.28.0

#25David G. Johnston
david.g.johnston@gmail.com
In reply to: Li Japin (#24)
Re: Terminate the idle sessions

On Mon, Nov 16, 2020 at 5:41 AM Li Japin <japinli@hotmail.com> wrote:

Thanks for your review! Attached.

Reading the doc changes:

I'd rather not name postgres_fdw explicitly, or at least not solely, as a
reason for setting this to zero. Additionally, using postgres_fdw within
the server doesn't cause issues, its using postgres_fdw and the remote
server having this setting set to zero that causes a problem.

<note>
Consider setting this for specific users instead of as a server default.
Client connections managed by connection poolers, or initiated indirectly
like those by a remote postgres_fdw using server, should probably be
excluded from this timeout.

Text within <para> should be indented one space (you missed both under
listitem).

I'd suggest a comment that aside from a bit of resource consumption idle
sessions do not interfere with the long-running stability of the server,
unlike idle-in-transaction sessions which are controlled by the other
configuration setting.

David J.

#26Li Japin
japinli@hotmail.com
In reply to: David G. Johnston (#25)
Re: Terminate the idle sessions

--
Best regards
Japin Li

On Nov 17, 2020, at 7:59 AM, David G. Johnston <david.g.johnston@gmail.com<mailto:david.g.johnston@gmail.com>> wrote:

On Mon, Nov 16, 2020 at 5:41 AM Li Japin <japinli@hotmail.com<mailto:japinli@hotmail.com>> wrote:
Thanks for your review! Attached.

Reading the doc changes:

I'd rather not name postgres_fdw explicitly, or at least not solely, as a reason for setting this to zero. Additionally, using postgres_fdw within the server doesn't cause issues, its using postgres_fdw and the remote server having this setting set to zero that causes a problem.

<note>
Consider setting this for specific users instead of as a server default. Client connections managed by connection poolers, or initiated indirectly like those by a remote postgres_fdw using server, should probably be excluded from this timeout.

Text within <para> should be indented one space (you missed both under listitem).

Thanks for your suggest! How about change document as follows:

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6c4e2a1fdc..23e691a7c5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8281,17 +8281,17 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </term>
       <listitem>
        <para>
-       Terminate any session that has been idle for longer than the specified amount of time.
+        Terminate any session that has been idle for longer than the specified amount of time.
        </para>
        <para>
-       If this value is specified without units, it is taken as milliseconds.
-       A value of zero (the default) disables the timeout.
+        If this value is specified without units, it is taken as milliseconds.
+        A value of zero (the default) disables the timeout.
        </para>
        <note>
         <para>
-         This parameter should be set to zero if you use postgres_fdw or some
-         connection-pooling software, because connections might be closed unexpectedly.
+         This parameter should be set to zero if you use some connection-pooling software, or
+         PostgreSQL servers used by postgres_fdw, because connections might be closed unexpectedly.
         </para>
        </note>

I'd suggest a comment that aside from a bit of resource consumption idle sessions do not interfere with the long-running stability of the server, unlike idle-in-transaction sessions which are controlled by the other configuration setting.

Could you please explain how the idle-in-transaction interfere the long-running stability?

--
Best regards
Japin Li

#27David G. Johnston
david.g.johnston@gmail.com
In reply to: Li Japin (#26)
Re: Terminate the idle sessions

On Monday, November 16, 2020, Li Japin <japinli@hotmail.com> wrote:

<note>
Consider setting this for specific users instead of as a server default.
Client connections managed by connection poolers, or initiated indirectly
like those by a remote postgres_fdw using server, should probably be
excluded from this timeout.

<note>

<para>
-         This parameter should be set to zero if you use postgres_fdw or
some
-         connection-pooling software, because connections might be closed
unexpectedly.
+         This parameter should be set to zero if you use some
connection-pooling software, or
+         PostgreSQL servers used by postgres_fdw, because connections
might be closed unexpectedly.
</para>
</note>

Prefer mine, “or pg servers used by postgres_fdw”, doesn’t flow.

Could you please explain how the idle-in-transaction interfere the
long-running stability?

From the docs (next section):

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 Section 24.1
<https://www.postgresql.org/docs/13/routine-vacuuming.html&gt; for more
details about this.

David J.

#28Li Japin
japinli@hotmail.com
In reply to: David G. Johnston (#27)
2 attachment(s)
Re: Terminate the idle sessions

On Nov 17, 2020, at 10:53 AM, David G. Johnston <david.g.johnston@gmail.com<mailto:david.g.johnston@gmail.com>> wrote:

On Monday, November 16, 2020, Li Japin <japinli@hotmail.com<mailto:japinli@hotmail.com>> wrote:

<note>
Consider setting this for specific users instead of as a server default. Client connections managed by connection poolers, or initiated indirectly like those by a remote postgres_fdw using server, should probably be excluded from this timeout.

        <note>
         <para>
-         This parameter should be set to zero if you use postgres_fdw or some
-         connection-pooling software, because connections might be closed unexpectedly.
+         This parameter should be set to zero if you use some connection-pooling software, or
+         PostgreSQL servers used by postgres_fdw, because connections might be closed unexpectedly.
         </para>
        </note>

Prefer mine, “or pg servers used by postgres_fdw”, doesn’t flow.

Could you please explain how the idle-in-transaction interfere the long-running stability?

From the docs (next section):

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 Section 24.1<https://www.postgresql.org/docs/13/routine-vacuuming.html&gt; for more details about this.

Thanks David! Attached.

--
Best regards
Japin Li

Attachments:

v6-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v6-0001-Allow-terminating-the-idle-sessions.patchDownload
From e2a70c2e3e20aa93a287354ab0cd5446bbe51c78 Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v6 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
some connection-pooling software, or pg servers used by postgres_fdw,
because connections might be closed unexpectedly.
---
 doc/src/sgml/config.sgml                      | 29 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 ++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 +++++++
 src/backend/utils/misc/guc.c                  | 11 +++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 82 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a632cf98ba..b71a116be3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8276,6 +8276,35 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+        If this value is specified without units, it is taken as milliseconds.
+        A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use some connection-pooling software,
+         or pg servers used by postgres_fdw, because connections might be closed unexpectedly.
+        </para>
+        <para>
+         Aside from a bit of resource consumption idle sessions do not interfere with the
+         long-running stability of the server.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index d1738c65f5..5fd3e79e72 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..313900e998 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1067f58f51..a6f3077b7c 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -369,6 +369,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..15eeb0ed2c 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -31,6 +31,7 @@ typedef enum TimeoutId
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
-- 
2.28.0

v6-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v6-0002-Optimize-setitimer-usage.patchDownload
From 58ab9d8e674366e61c3427ce7c58c3ac26e850cb Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v6 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 42 ++++++++++++++++++--------------
 1 file changed, 24 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..b5027815db 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -591,8 +610,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +621,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.28.0

#29kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#28)
RE: Terminate the idle sessions

Dear Li, David,

Additionally, using postgres_fdw within the server doesn't cause issues,
its using postgres_fdw and the remote server having this setting set to zero that causes a problem.

I didn't know the fact that postgres_fdw can use within the server... Thanks.

I read optimize-setitimer patch, and looks basically good. I put what I understanding,
so please confirm it whether your implementation is correct.
(Maybe I missed some simultaneities, so please review anyone...)

[besic consept]

sigalrm_due_at means the time that interval timer will ring, and sigalrm_delivered means who calls schedule_alarm().
If fin_time of active_timeouts[0] is larger than or equal to sigalrm_due_at,
stop calling setitimer because handle_sig_alarm() will be call sooner.

[when call setitimer]

In the attached patch, setitimer() will be only called the following scenarios:

* when handle_sig_alarm() is called due to the pqsignal
* when a timeout is registered and its fin_time is later than active_timeous[0]
* when disable a timeout
* when handle_sig_alarm() is interrupted and rescheduled(?)

According to comments, handle_sig_alarm() may be interrupted because of the ereport.
I think if handle_sig_alarm() is interrupted before subsutituting sigalrm_due_at to true,
interval timer will be never set. Is it correct, or is my assumption wrong?

Lastly, I found that setitimer is obsolete and should change to another one. According to my man page:

```
POSIX.1-2001, SVr4, 4.4BSD (this call first appeared in 4.2BSD).
POSIX.1-2008 marks getitimer() and setitimer() obsolete,
recommending the use of the POSIX timers API (timer_gettime(2), timer_settime(2), etc.) instead.
```

Do you have an opinion for this? I think it should be changed
if all platform can support timer_settime system call, but this fix affects all timeouts,
so more considerations might be needed.

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

#30Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#29)
Re: Terminate the idle sessions

On Nov 17, 2020, at 2:07 PM, kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> wrote:

Dear Li, David,

Additionally, using postgres_fdw within the server doesn't cause issues,
its using postgres_fdw and the remote server having this setting set to zero that causes a problem.

I didn't know the fact that postgres_fdw can use within the server... Thanks.

I read optimize-setitimer patch, and looks basically good. I put what I understanding,
so please confirm it whether your implementation is correct.
(Maybe I missed some simultaneities, so please review anyone...)

[besic consept]

sigalrm_due_at means the time that interval timer will ring, and sigalrm_delivered means who calls schedule_alarm().
If fin_time of active_timeouts[0] is larger than or equal to sigalrm_due_at,
stop calling setitimer because handle_sig_alarm() will be call sooner.

Agree. The sigalrm_delivered means a timer has been handled by handle_sig_alarm(), so we should call setitimer() in next schedule_alarm().

[when call setitimer]

In the attached patch, setitimer() will be only called the following scenarios:

* when handle_sig_alarm() is called due to the pqsignal
* when a timeout is registered and its fin_time is later than active_timeous[0]
* when disable a timeout
* when handle_sig_alarm() is interrupted and rescheduled(?)

According to comments, handle_sig_alarm() may be interrupted because of the ereport.
I think if handle_sig_alarm() is interrupted before subsutituting sigalrm_due_at to true,
interval timer will be never set. Is it correct, or is my assumption wrong?

I’m not familiar with the system interrupt, however, the sigalrm_due_at is subsutitue between HOLD_INTERRUPTS()
and RESUM_INTERRUPTS(), so I think it cannot be interrupted. The following comments comes from miscadmin.h.

The HOLD_INTERRUPTS() and RESUME_INTERRUPTS() macros
allow code to ensure that no cancel or die interrupt will be accepted,
even if CHECK_FOR_INTERRUPTS() gets called in a subroutine. The interrupt
will be held off until CHECK_FOR_INTERRUPTS() is done outside any
HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() section.

Lastly, I found that setitimer is obsolete and should change to another one. According to my man page:

```
POSIX.1-2001, SVr4, 4.4BSD (this call first appeared in 4.2BSD).
POSIX.1-2008 marks getitimer() and setitimer() obsolete,
recommending the use of the POSIX timers API (timer_gettime(2), timer_settime(2), etc.) instead.
```

Do you have an opinion for this? I think it should be changed
if all platform can support timer_settime system call, but this fix affects all timeouts,
so more considerations might be needed.

Not sure! I find that Win32 do not support setitimer(), PostgreSQL emulate setitimer() by creating a persistent thread to handle
the timer setting and notification upon timeout.

So if we want to replace it, I think we should open a new thread to achieve this.

--
Best regards
Japin Li

#31kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#30)
RE: Terminate the idle sessions

Dear Li,

I’m not familiar with the system interrupt, however,
the sigalrm_due_at is subsutitue between HOLD_INTERRUPTS()
and RESUM_INTERRUPTS(), so I think it cannot be interrupted.
The following comments comes from miscadmin.h.

Right, but how about before HOLD_INTERRUPTS()?
If so, only calling handle_sig_alarm() is occurred, and
Setitimer will not be set, I think.

Not sure! I find that Win32 do not support setitimer(),
PostgreSQL emulate setitimer() by creating a persistent thread to handle
the timer setting and notification upon timeout.

Yes, set/getitimer() is the systemcall, and
implemented only in the unix-like system.
But I rethink that such a fix is categorized in the refactoring and
we should separate topic. Hence we can ignore here.

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

From: Li Japin <japinli@hotmail.com>
Sent: Tuesday, November 17, 2020 6:02 PM
To: Kuroda, Hayato/黒田 隼人 <kuroda.hayato@fujitsu.com>
Cc: David G. Johnston <david.g.johnston@gmail.com>; Kyotaro Horiguchi <horikyota.ntt@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; bharath.rupireddyforpostgres@gmail.com; pgsql-hackers@lists.postgresql.org
Subject: Re: Terminate the idle sessions

On Nov 17, 2020, at 2:07 PM, mailto:kuroda.hayato@fujitsu.com wrote:

Dear Li, David,
 

Additionally, using postgres_fdw within the server doesn't cause issues,
its using postgres_fdw and the remote server having this setting set to zero that causes a problem.

 
I didn't know the fact that postgres_fdw can use within the server... Thanks.
 
I read optimize-setitimer patch, and looks basically good. I put what I understanding,
so please confirm it whether your implementation is correct.
(Maybe I missed some simultaneities, so please review anyone...)
 
[besic consept]
 
sigalrm_due_at means the time that interval timer will ring, and sigalrm_delivered means who calls schedule_alarm().
If fin_time of active_timeouts[0] is larger than or equal to sigalrm_due_at,
stop calling setitimer because handle_sig_alarm() will be call sooner.
 

Agree. The sigalrm_delivered means a timer has been handled by handle_sig_alarm(), so we should call setitimer() in next schedule_alarm().

[when call setitimer]
 
In the attached patch, setitimer() will be only called the following scenarios:
 
* when handle_sig_alarm() is called due to the pqsignal
* when a timeout is registered and its fin_time is later than active_timeous[0]
* when disable a timeout
* when handle_sig_alarm() is interrupted and rescheduled(?)
 
According to comments, handle_sig_alarm() may be interrupted because of the ereport.
I think if handle_sig_alarm() is interrupted before subsutituting sigalrm_due_at to true,
interval timer will be never set. Is it correct, or is my assumption wrong?
 

I’m not familiar with the system interrupt, however, the sigalrm_due_at is subsutitue between HOLD_INTERRUPTS()
and RESUM_INTERRUPTS(), so I think it cannot be interrupted.  The following comments comes from miscadmin.h.

The HOLD_INTERRUPTS() and RESUME_INTERRUPTS() macros
allow code to ensure that no cancel or die interrupt will be accepted,
even if CHECK_FOR_INTERRUPTS() gets called in a subroutine.  The interrupt
will be held off until CHECK_FOR_INTERRUPTS() is done outside any
HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() section.

Lastly, I found that setitimer is obsolete and should change to another one. According to my man page:
 
```
POSIX.1-2001, SVr4, 4.4BSD (this call first appeared in 4.2BSD).
POSIX.1-2008 marks getitimer() and setitimer() obsolete,
recommending the use of the POSIX timers API (timer_gettime(2), timer_settime(2), etc.) instead.
```
 
Do you have an opinion for this? I think it should be changed
if all platform can support timer_settime system call, but this fix affects all timeouts,
so more considerations might be needed.
 

Not sure! I find that Win32 do not support setitimer(), PostgreSQL emulate setitimer() by creating a persistent thread to handle
the timer setting and notification upon timeout.

So if we want to replace it, I think we should open a new thread to achieve this.

--
Best regards
Japin Li

#32Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#31)
Re: Terminate the idle sessions

On Nov 18, 2020, at 10:40 AM, kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> wrote:

I’m not familiar with the system interrupt, however,
the sigalrm_due_at is subsutitue between HOLD_INTERRUPTS()
and RESUM_INTERRUPTS(), so I think it cannot be interrupted.
The following comments comes from miscadmin.h.

Right, but how about before HOLD_INTERRUPTS()?
If so, only calling handle_sig_alarm() is occurred, and
Setitimer will not be set, I think.

Yeah, it might be occurred. Any suggestions to fix it?

--
Best regards
Japin Li

#33kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#32)
RE: Terminate the idle sessions

Dear Li,

Yeah, it might be occurred. Any suggestions to fix it?

Oops.. I forgot putting my suggestion. Sorry.
How about substituting sigalrm_delivered to true in the reschedule_timeouts()?
Maybe this processing looks strange, so some comments should be put too.
Here is an example:

```diff
@@ -423,7 +423,14 @@ reschedule_timeouts(void)

        /* Reschedule the interrupt, if any timeouts remain active. */
        if (num_active_timeouts > 0)
+       {
+               /*
+                * sigalrm_delivered is set to true,
+                * because any intrreputions might be occured.
+                */
+               sigalrm_delivered = true;
                schedule_alarm(GetCurrentTimestamp());
+       }
 }
```

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

#34Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#33)
2 attachment(s)
Re: Terminate the idle sessions

On Nov 18, 2020, at 2:22 PM, kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> wrote:

Oops.. I forgot putting my suggestion. Sorry.
How about substituting sigalrm_delivered to true in the reschedule_timeouts()?
Maybe this processing looks strange, so some comments should be put too.
Here is an example:

```diff
@@ -423,7 +423,14 @@ reschedule_timeouts(void)

       /* Reschedule the interrupt, if any timeouts remain active. */
       if (num_active_timeouts > 0)
+       {
+               /*
+                * sigalrm_delivered is set to true,
+                * because any intrreputions might be occured.
+                */
+               sigalrm_delivered = true;
               schedule_alarm(GetCurrentTimestamp());
+       }
}
```

Thanks for your suggestion. Attached!

--
Best regards
Japin Li

Attachments:

v7-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v7-0001-Allow-terminating-the-idle-sessions.patchDownload
From d49aca925fc7e200343d2ebfaf9b18694662aef0 Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v7 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
some connection-pooling software, or pg servers used by postgres_fdw,
because connections might be closed unexpectedly.
---
 doc/src/sgml/config.sgml                      | 29 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 25 ++++++++++++++++
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 +++++++
 src/backend/utils/misc/guc.c                  | 11 +++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 82 insertions(+)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a632cf98ba..b71a116be3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8276,6 +8276,35 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+        If this value is specified without units, it is taken as milliseconds.
+        A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use some connection-pooling software,
+         or pg servers used by postgres_fdw, because connections might be closed unexpectedly.
+        </para>
+        <para>
+         Aside from a bit of resource consumption idle sessions do not interfere with the
+         long-running stability of the server.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index d1738c65f5..5fd3e79e72 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..313900e998 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 1067f58f51..a6f3077b7c 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -369,6 +369,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..15eeb0ed2c 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -31,6 +31,7 @@ typedef enum TimeoutId
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
-- 
2.28.0

v7-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v7-0002-Optimize-setitimer-usage.patchDownload
From 70d7139b4706d205898a1363e9504e94c07e1482 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v7 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 49 ++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..4effcf3be6 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -423,7 +442,14 @@ reschedule_timeouts(void)
 
 	/* Reschedule the interrupt, if any timeouts remain active. */
 	if (num_active_timeouts > 0)
+	{
+		/*
+		 * Any interruptions might be occured, so we should reschedule
+		 * the active timeouts.
+		 */
+		sigalrm_delivered = true;
 		schedule_alarm(GetCurrentTimestamp());
+	}
 }
 
 /*
@@ -591,8 +617,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +628,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.28.0

#35kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Li Japin (#34)
RE: Terminate the idle sessions

Dear Li,

Thanks for your suggestion.  Attached!

I prefer your comments:-).

I think this patch is mostly good.
I looked whole the codes again and I found the following comment in the PostgresMain():

```c
/*
* (5) turn off the idle-in-transaction timeout
*/
```

Please mention about idle-session timeout and check another comment.

Best Regards,
Hayato Kuroda

#36japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#35)
2 attachment(s)
Re: Terminate the idle sessions

hi, Kuroda

On 11/19/20 4:32 PM, kuroda.hayato@fujitsu.com wrote:

Dear Li,

Thanks for your suggestion.  Attached!

I prefer your comments:-).

I think this patch is mostly good.
I looked whole the codes again and I found the following comment in the PostgresMain():

```c
/*
* (5) turn off the idle-in-transaction timeout
*/
```

Please mention about idle-session timeout and check another comment.

Thanks! Add the comment for idle-session timeout.

--
Best regards
Japin Li

Attachments:

v8-0001-Allow-terminating-the-idle-sessions.patchtext/x-patch; charset=UTF-8; name=v8-0001-Allow-terminating-the-idle-sessions.patchDownload
From ca226701bd04d7c7f766776094610ef6a3e96279 Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v8 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
some connection-pooling software, or pg servers used by postgres_fdw,
because connections might be closed unexpectedly.
---
 doc/src/sgml/config.sgml                      | 29 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 27 ++++++++++++++++-
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 +++++++
 src/backend/utils/misc/guc.c                  | 11 +++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index a632cf98ba..b71a116be3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8276,6 +8276,35 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+        If this value is specified without units, it is taken as milliseconds.
+        A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use some connection-pooling software,
+         or pg servers used by postgres_fdw, because connections might be closed unexpectedly.
+        </para>
+        <para>
+         Aside from a bit of resource consumption idle sessions do not interfere with the
+         long-running stability of the server.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index d1738c65f5..5fd3e79e72 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..ba2369b72d 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4259,7 +4278,7 @@ PostgresMain(int argc, char *argv[],
 		DoingCommandRead = false;
 
 		/*
-		 * (5) turn off the idle-in-transaction timeout
+		 * (5) turn off the idle-in-transaction and idle-session timeout
 		 */
 		if (disable_idle_in_transaction_timeout)
 		{
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 00bb244340..75059539a7 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -374,6 +374,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..15eeb0ed2c 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -31,6 +31,7 @@ typedef enum TimeoutId
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
-- 
2.28.0

v8-0002-Optimize-setitimer-usage.patchtext/x-patch; charset=UTF-8; name=v8-0002-Optimize-setitimer-usage.patchDownload
From 35dcb4eab26d14d0d3897e25459fbd67826bbdea Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v8 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 49 ++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..4effcf3be6 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -423,7 +442,14 @@ reschedule_timeouts(void)
 
 	/* Reschedule the interrupt, if any timeouts remain active. */
 	if (num_active_timeouts > 0)
+	{
+		/*
+		 * Any interruptions might be occured, so we should reschedule
+		 * the active timeouts.
+		 */
+		sigalrm_delivered = true;
 		schedule_alarm(GetCurrentTimestamp());
+	}
 }
 
 /*
@@ -591,8 +617,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +628,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.28.0

#37kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: japin (#36)
RE: Terminate the idle sessions

Dear Li,

Thanks! Add the comment for idle-session timeout.

I confirmed it. OK.

I don't have any comments anymore. If no one has,
I will change the status few days later.
Other comments or suggestions to him?

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

#38kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: kuroda.hayato@fujitsu.com (#37)
RE: Terminate the idle sessions

No one have any comments, patch tester says OK, and I think this works well.
I changed status to "Ready for Committer."

Hayato Kuroda
FUJITSU LIMITED

-----Original Message-----
From: kuroda.hayato@fujitsu.com <kuroda.hayato@fujitsu.com>
Sent: Friday, November 20, 2020 11:05 AM
To: 'japin' <japinli@hotmail.com>
Cc: David G. Johnston <david.g.johnston@gmail.com>; Kyotaro Horiguchi <horikyota.ntt@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; bharath.rupireddyforpostgres@gmail.com; pgsql-hackers@lists.postgresql.org
Subject: RE: Terminate the idle sessions

Dear Li,

Thanks! Add the comment for idle-session timeout.

I confirmed it. OK.

I don't have any comments anymore. If no one has,
I will change the status few days later.
Other comments or suggestions to him?

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

#39David G. Johnston
david.g.johnston@gmail.com
In reply to: kuroda.hayato@fujitsu.com (#38)
Re: Terminate the idle sessions

On Mon, Nov 23, 2020 at 5:02 PM kuroda.hayato@fujitsu.com <
kuroda.hayato@fujitsu.com> wrote:

No one have any comments, patch tester says OK, and I think this works
well.
I changed status to "Ready for Committer."

Some proof-reading:

v8-0001

Documentation:

My suggestion wasn't taken for the first note paragraph (review/author
disagreement) and the current has the following issues:

"if you use some connection-pooling" software doesn't need the word "some"
Don't substitute "pg" for the name of the product, PostgreSQL.
The word "used" is a more stylistic dislike, but "connected to using
postgres_fdw" would be a better choice IMO.

Code (minor, but if you are in there anyway):

(5) turn off ... timeout (there are now two, timeouts should be plural)

v8-0002 - No suggestions

David J.

#40Li Japin
japinli@hotmail.com
In reply to: kuroda.hayato@fujitsu.com (#38)
Re: Terminate the idle sessions

Hi, Kuroda

Thank for your review.

On Nov 24, 2020, at 8:01 AM, kuroda.hayato@fujitsu.com wrote:

No one have any comments, patch tester says OK, and I think this works well.
I changed status to "Ready for Committer."

Hayato Kuroda
FUJITSU LIMITED

-----Original Message-----
From: kuroda.hayato@fujitsu.com <kuroda.hayato@fujitsu.com>
Sent: Friday, November 20, 2020 11:05 AM
To: 'japin' <japinli@hotmail.com>
Cc: David G. Johnston <david.g.johnston@gmail.com>; Kyotaro Horiguchi <horikyota.ntt@gmail.com>; Thomas Munro <thomas.munro@gmail.com>; bharath.rupireddyforpostgres@gmail.com; pgsql-hackers@lists.postgresql.org
Subject: RE: Terminate the idle sessions

Dear Li,

Thanks! Add the comment for idle-session timeout.

I confirmed it. OK.

I don't have any comments anymore. If no one has,
I will change the status few days later.
Other comments or suggestions to him?

Best Regards,
Hayato Kuroda
FUJITSU LIMITED

--
Best regards
Japin Li

#41Li Japin
japinli@hotmail.com
In reply to: David G. Johnston (#39)
Re: Terminate the idle sessions

Hi, David

Thanks for your suggestion!

On Nov 24, 2020, at 11:39 AM, David G. Johnston <david.g.johnston@gmail.com<mailto:david.g.johnston@gmail.com>> wrote:

On Mon, Nov 23, 2020 at 5:02 PM kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com> <kuroda.hayato@fujitsu.com<mailto:kuroda.hayato@fujitsu.com>> wrote:
No one have any comments, patch tester says OK, and I think this works well.
I changed status to "Ready for Committer."
Some proof-reading:

v8-0001

Documentation:

My suggestion wasn't taken for the first note paragraph (review/author disagreement) and the current has the following issues:

Sorry for ignoring this suggestion.

"if you use some connection-pooling" software doesn't need the word "some"
Don't substitute "pg" for the name of the product, PostgreSQL.
The word "used" is a more stylistic dislike, but "connected to using postgres_fdw" would be a better choice IMO.

Code (minor, but if you are in there anyway):

How about use “foreign-data wrapper” replace “postgres_fdw”?

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b71a116be3..a3a50e7bdb 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8293,8 +8293,9 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
        <note>
         <para>
-         This parameter should be set to zero if you use some connection-pooling software,
-         or pg servers used by postgres_fdw, because connections might be closed unexpectedly.
+         This parameter should be set to zero if you use connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected to using foreign-data
+         wrapper, because connections might be closed unexpectedly.
         </para>
         <para>
          Aside from a bit of resource consumption idle sessions do not interfere with the

(5) turn off ... timeout (there are now two, timeouts should be plural)

Fixed.

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ba2369b72d..bcf8c610fd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4278,7 +4278,7 @@ PostgresMain(int argc, char *argv[],
                DoingCommandRead = false;
                /*
-                * (5) turn off the idle-in-transaction and idle-session timeout
+                * (5) turn off the idle-in-transaction and idle-session timeouts
                 */
                if (disable_idle_in_transaction_timeout)
                {

I will send a new patch if there is not other comments.

--
Best Regards,
Japin Li

#42David G. Johnston
david.g.johnston@gmail.com
In reply to: Li Japin (#41)
Re: Terminate the idle sessions

On Mon, Nov 23, 2020 at 11:22 PM Li Japin <japinli@hotmail.com> wrote:

How about use “foreign-data wrapper” replace “postgres_fdw”?

I don't see much value in avoiding mentioning that specific term - my
proposal turned it into an example instead of being exclusive.

-         This parameter should be set to zero if you use some
connection-pooling software,
-         or pg servers used by postgres_fdw, because connections might be
closed unexpectedly.
+         This parameter should be set to zero if you use
connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected to
using foreign-data
+         wrapper, because connections might be closed unexpectedly.
</para>

Maybe:

+ or your PostgreSQL server receives connection from postgres_fdw or
similar middleware.
+ Such software is expected to self-manage its connections.
David J.
#43Li Japin
japinli@hotmail.com
In reply to: David G. Johnston (#42)
2 attachment(s)
Re: Terminate the idle sessions

On Nov 24, 2020, at 11:20 PM, David G. Johnston <david.g.johnston@gmail.com<mailto:david.g.johnston@gmail.com>> wrote:

On Mon, Nov 23, 2020 at 11:22 PM Li Japin <japinli@hotmail.com<mailto:japinli@hotmail.com>> wrote:

How about use “foreign-data wrapper” replace “postgres_fdw”?

I don't see much value in avoiding mentioning that specific term - my proposal turned it into an example instead of being exclusive.

-         This parameter should be set to zero if you use some connection-pooling software,
-         or pg servers used by postgres_fdw, because connections might be closed unexpectedly.
+         This parameter should be set to zero if you use connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected to using foreign-data
+         wrapper, because connections might be closed unexpectedly.
         </para>

Maybe:

+ or your PostgreSQL server receives connection from postgres_fdw or similar middleware.
+ Such software is expected to self-manage its connections.

Thank you for your suggestion and patient! Fixed.

```
+        <para>
+         This parameter should be set to zero if you use connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected to using postgres_fdw
+         or similar middleware (such software is expected to self-manage its connections),
+         because connections might be closed unexpectedly.
+        </para>
```

--
Best regards
Japin Li

Attachments:

v9-0001-Allow-terminating-the-idle-sessions.patchapplication/octet-stream; name=v9-0001-Allow-terminating-the-idle-sessions.patchDownload
From be89a63163f44178b7418e88ebf1f6e7bff7e65c Mon Sep 17 00:00:00 2001
From: japinli <japinli@hotmail.com>
Date: Sun, 15 Nov 2020 17:43:30 +0800
Subject: [PATCH v9 1/2] Allow terminating the idle sessions

Terminate any session that has been idle for longer than the specified
amount of time.  Note that this values should be set to zero if you use
connection-pooling software, or PostgreSQL servers connected to using
postgres_fdw or similar middleware, because connections might be closed
unexpectedly.
---
 doc/src/sgml/config.sgml                      | 31 +++++++++++++++++++
 src/backend/storage/lmgr/proc.c               |  1 +
 src/backend/tcop/postgres.c                   | 27 +++++++++++++++-
 src/backend/utils/errcodes.txt                |  1 +
 src/backend/utils/init/globals.c              |  1 +
 src/backend/utils/init/postinit.c             | 10 ++++++
 src/backend/utils/misc/guc.c                  | 11 +++++++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/miscadmin.h                       |  1 +
 src/include/storage/proc.h                    |  1 +
 src/include/utils/timeout.h                   |  1 +
 11 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 3795c57004..27e21ad560 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8273,6 +8273,37 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-idle-session-timeout" xreflabel="idle_session_timeout">
+      <term><varname>idle_session_timeout</varname> (<type>integer</type>)
+      <indexterm>
+       <primary><varname>idle_session_timeout</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Terminate any session that has been idle for longer than the specified amount of time.
+       </para>
+       <para>
+        If this value is specified without units, it is taken as milliseconds.
+        A value of zero (the default) disables the timeout.
+       </para>
+
+       <note>
+        <para>
+         This parameter should be set to zero if you use connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected to using postgres_fdw
+         or similar middleware (such software is expected to self-manage its connections),
+         because connections might be closed unexpectedly.
+        </para>
+        <para>
+         Aside from a bit of resource consumption idle sessions do not interfere with the
+         long-running stability of the server.
+        </para>
+       </note>
+
+      </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>
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 7dc3911590..a93e56c2db 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -60,6 +60,7 @@
 int			DeadlockTimeout = 1000;
 int			StatementTimeout = 0;
 int			LockTimeout = 0;
+int			IdleSessionTimeout = 0;
 int			IdleInTransactionSessionTimeout = 0;
 bool		log_lock_waits = false;
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7c5f7c775b..bcf8c610fd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3204,6 +3204,16 @@ ProcessInterrupts(void)
 
 	}
 
+	if (IdleSessionTimeoutPending)
+	{
+		if (IdleSessionTimeout > 0)
+			ereport(FATAL,
+					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
+					 errmsg("terminating connection due to idle-session timeout")));
+		else
+			IdleSessionTimeoutPending = false;
+	}
+
 	if (ProcSignalBarrierPending)
 		ProcessProcSignalBarrier();
 
@@ -3779,6 +3789,7 @@ PostgresMain(int argc, char *argv[],
 	sigjmp_buf	local_sigjmp_buf;
 	volatile bool send_ready_for_query = true;
 	bool		disable_idle_in_transaction_timeout = false;
+	bool		disable_idle_session_timeout = false;
 
 	/* Initialize startup process environment if necessary. */
 	if (!IsUnderPostmaster)
@@ -4227,6 +4238,14 @@ PostgresMain(int argc, char *argv[],
 
 				set_ps_display("idle");
 				pgstat_report_activity(STATE_IDLE, NULL);
+
+				/* Start the idle-session timer */
+				if (IdleSessionTimeout > 0)
+				{
+					disable_idle_session_timeout = true;
+					enable_timeout_after(IDLE_SESSION_TIMEOUT,
+										 IdleSessionTimeout);
+				}
 			}
 
 			ReadyForQuery(whereToSendOutput);
@@ -4259,7 +4278,7 @@ PostgresMain(int argc, char *argv[],
 		DoingCommandRead = false;
 
 		/*
-		 * (5) turn off the idle-in-transaction timeout
+		 * (5) turn off the idle-in-transaction and idle-session timeouts
 		 */
 		if (disable_idle_in_transaction_timeout)
 		{
@@ -4267,6 +4286,12 @@ PostgresMain(int argc, char *argv[],
 			disable_idle_in_transaction_timeout = false;
 		}
 
+		if (disable_idle_session_timeout)
+		{
+			disable_timeout(IDLE_SESSION_TIMEOUT, false);
+			disable_idle_session_timeout = false;
+		}
+
 		/*
 		 * (6) check for any other interesting events that happened while we
 		 * slept.
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c79312ed03..d5935a2ca9 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -109,6 +109,7 @@ Section: Class 08 - Connection Exception
 08004    E    ERRCODE_SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION      sqlserver_rejected_establishment_of_sqlconnection
 08007    E    ERRCODE_TRANSACTION_RESOLUTION_UNKNOWN                         transaction_resolution_unknown
 08P01    E    ERRCODE_PROTOCOL_VIOLATION                                     protocol_violation
+08008    E    ERRCODE_IDLE_SESSION_TIMEOUT                                   idle_session_timeout
 
 Section: Class 09 - Triggered Action Exception
 
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 6ab8216839..03bfd88d2c 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -31,6 +31,7 @@ volatile sig_atomic_t InterruptPending = false;
 volatile sig_atomic_t QueryCancelPending = false;
 volatile sig_atomic_t ProcDiePending = false;
 volatile sig_atomic_t ClientConnectionLost = false;
+volatile sig_atomic_t IdleSessionTimeoutPending = false;
 volatile sig_atomic_t IdleInTransactionSessionTimeoutPending = false;
 volatile sig_atomic_t ProcSignalBarrierPending = false;
 volatile uint32 InterruptHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index f2dd8e4914..fbe758061b 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -71,6 +71,7 @@ static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
 static void StatementTimeoutHandler(void);
 static void LockTimeoutHandler(void);
+static void IdleSessionTimeoutHandler(void);
 static void IdleInTransactionSessionTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
@@ -628,6 +629,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
 		RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLockAlert);
 		RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
 		RegisterTimeout(LOCK_TIMEOUT, LockTimeoutHandler);
+		RegisterTimeout(IDLE_SESSION_TIMEOUT, IdleSessionTimeoutHandler);
 		RegisterTimeout(IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
 						IdleInTransactionSessionTimeoutHandler);
 	}
@@ -1236,6 +1238,14 @@ LockTimeoutHandler(void)
 	kill(MyProcPid, SIGINT);
 }
 
+static void
+IdleSessionTimeoutHandler(void)
+{
+	IdleSessionTimeoutPending = true;
+	InterruptPending = true;
+	SetLatch(MyLatch);
+}
+
 static void
 IdleInTransactionSessionTimeoutHandler(void)
 {
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..e6df047b70 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2503,6 +2503,17 @@ static struct config_int ConfigureNamesInt[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"idle_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+			gettext_noop("Sets the maximum allowed duration of any idling session."),
+			gettext_noop("A value of 0 turns off the timeout."),
+			GUC_UNIT_MS
+		},
+		&IdleSessionTimeout,
+		0, 0, INT_MAX,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"idle_in_transaction_session_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the maximum allowed duration of any idling transaction."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9cb571f7cc..6e3f14f234 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -664,6 +664,7 @@
 #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
+#idle_session_timeout = 0		# in milliseconds, 0 is disabled
 #vacuum_freeze_min_age = 50000000
 #vacuum_freeze_table_age = 150000000
 #vacuum_multixact_freeze_min_age = 5000000
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 72e3352398..995b603899 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -81,6 +81,7 @@
 extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
 extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
+extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
 extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
 
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 00bb244340..75059539a7 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -374,6 +374,7 @@ extern PGPROC *PreparedXactProcs;
 extern PGDLLIMPORT int DeadlockTimeout;
 extern PGDLLIMPORT int StatementTimeout;
 extern PGDLLIMPORT int LockTimeout;
+extern PGDLLIMPORT int IdleSessionTimeout;
 extern PGDLLIMPORT int IdleInTransactionSessionTimeout;
 extern bool log_lock_waits;
 
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 83a15f6795..15eeb0ed2c 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -31,6 +31,7 @@ typedef enum TimeoutId
 	STANDBY_TIMEOUT,
 	STANDBY_LOCK_TIMEOUT,
 	IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
+	IDLE_SESSION_TIMEOUT,
 	/* First user-definable timeout reason */
 	USER_TIMEOUT,
 	/* Maximum number of timeout reasons */
-- 
2.24.1

v9-0002-Optimize-setitimer-usage.patchapplication/octet-stream; name=v9-0002-Optimize-setitimer-usage.patchDownload
From 3d5d215bd9408ed9f26a82fe8c6c362a02e140f2 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Aug 2020 23:22:19 +1200
Subject: [PATCH v9 2/2] Optimize setitimer() usage.

Don't call setitimer() so often.  Instead, let a preexisting alarm expire
and then request a new one as requried.  It's better to take a few extra
spurious alarm interrupts at a rate that's a function of the timeout
setting than to have a system call rate that's a function of statement
execution frequency.

XXX:  Need to review this carefully for races, perhaps involving
reentrancy.
---
 src/backend/utils/misc/timeout.c | 49 ++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 18 deletions(-)

diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index f1c9518b0c..4effcf3be6 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -51,6 +51,13 @@ static bool all_timeouts_initialized = false;
 static volatile int num_active_timeouts = 0;
 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
 
+/*
+ * State used to avoid installing a new timer interrupt when the previous one
+ * hasn't fired yet, but isn't too late.
+ */
+static TimestampTz sigalrm_due_at = PG_INT64_MAX;
+static volatile sig_atomic_t sigalrm_delivered = false;
+
 /*
  * Flag controlling whether the signal handler is allowed to do anything.
  * We leave this "false" when we're not expecting interrupts, just in case.
@@ -195,12 +202,13 @@ schedule_alarm(TimestampTz now)
 		struct itimerval timeval;
 		long		secs;
 		int			usecs;
+		TimestampTz	nearest_timeout;
 
 		MemSet(&timeval, 0, sizeof(struct itimerval));
 
 		/* Get the time remaining till the nearest pending timeout */
-		TimestampDifference(now, active_timeouts[0]->fin_time,
-							&secs, &usecs);
+		nearest_timeout = active_timeouts[0]->fin_time;
+		TimestampDifference(now, nearest_timeout, &secs, &usecs);
 
 		/*
 		 * It's possible that the difference is less than a microsecond;
@@ -244,9 +252,18 @@ schedule_alarm(TimestampTz now)
 		 */
 		enable_alarm();
 
+		/*
+		 * Try to avoid having to set the interval timer, if we already know
+		 * that there is an undelivered signal due at the same time or sooner.
+		 */
+		if (nearest_timeout >= sigalrm_due_at && !sigalrm_delivered)
+			return;
+
 		/* Set the alarm timer */
 		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
 			elog(FATAL, "could not enable SIGALRM timer: %m");
+		sigalrm_due_at = nearest_timeout;
+		sigalrm_delivered = false;
 	}
 }
 
@@ -266,6 +283,8 @@ handle_sig_alarm(SIGNAL_ARGS)
 {
 	int			save_errno = errno;
 
+	sigalrm_delivered = true;
+
 	/*
 	 * Bump the holdoff counter, to make sure nothing we call will process
 	 * interrupts directly. No timeout handler should do that, but these
@@ -423,7 +442,14 @@ reschedule_timeouts(void)
 
 	/* Reschedule the interrupt, if any timeouts remain active. */
 	if (num_active_timeouts > 0)
+	{
+		/*
+		 * Any interruptions might be occured, so we should reschedule
+		 * the active timeouts.
+		 */
+		sigalrm_delivered = true;
 		schedule_alarm(GetCurrentTimestamp());
+	}
 }
 
 /*
@@ -591,8 +617,9 @@ disable_timeouts(const DisableTimeoutParams *timeouts, int count)
 }
 
 /*
- * Disable SIGALRM and remove all timeouts from the active list,
- * and optionally reset their timeout indicators.
+ * Remove all timeouts from the active list, and optionally reset their timeout
+ * indicators.  Leave any existing itimer installed, because it may allow us to
+ * avoid having to set it again soon.
  */
 void
 disable_all_timeouts(bool keep_indicators)
@@ -601,20 +628,6 @@ disable_all_timeouts(bool keep_indicators)
 
 	disable_alarm();
 
-	/*
-	 * Only bother to reset the timer if we think it's active.  We could just
-	 * let the interrupt happen anyway, but it's probably a bit cheaper to do
-	 * setitimer() than to let the useless interrupt happen.
-	 */
-	if (num_active_timeouts > 0)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
-			elog(FATAL, "could not disable SIGALRM timer: %m");
-	}
-
 	num_active_timeouts = 0;
 
 	for (i = 0; i < MAX_TIMEOUTS; i++)
-- 
2.24.1

#44Anastasia Lubennikova
a.lubennikova@postgrespro.ru
In reply to: Li Japin (#43)
Re: Terminate the idle sessions

On 25.11.2020 05:18, Li Japin wrote:

On Nov 24, 2020, at 11:20 PM, David G. Johnston
<david.g.johnston@gmail.com <mailto:david.g.johnston@gmail.com>> wrote:

On Mon, Nov 23, 2020 at 11:22 PM Li Japin <japinli@hotmail.com
<mailto:japinli@hotmail.com>> wrote:

How about use “foreign-data wrapper” replace “postgres_fdw”?

I don't see much value in avoiding mentioning that specific term - my
proposal turned it into an example instead of being exclusive.

-         This parameter should be set to zero if you use some
connection-pooling software,
-         or pg servers used by postgres_fdw, because connections
might be closed unexpectedly.
+         This parameter should be set to zero if you use
connection-pooling software,
+         or <productname>PostgreSQL</productname> servers
connected to using foreign-data
+         wrapper, because connections might be closed unexpectedly.
         </para>

Maybe:

+ or your PostgreSQL server receives connection from postgres_fdw or 
similar middleware.
+ Such software is expected to self-manage its connections.

Thank you for your suggestion and patient! Fixed.

```
+        <para>
+         This parameter should be set to zero if you use 
connection-pooling software,
+         or <productname>PostgreSQL</productname> servers connected 
to using postgres_fdw
+         or similar middleware (such software is expected to 
self-manage its connections),
+         because connections might be closed unexpectedly.
+        </para>
```

--
Best regards
Japin Li

Status update for a commitfest entry.
As far as I see, all recommendations from reviewers were addressed in
the last version of the patch.

It passes CFbot successfully, so I move it to Ready For Committer.

--
Anastasia Lubennikova
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#45Tom Lane
tgl@sss.pgh.pa.us
In reply to: Li Japin (#43)
Re: Terminate the idle sessions

Li Japin <japinli@hotmail.com> writes:

[ v9-0001-Allow-terminating-the-idle-sessions.patch ]

I've reviewed and pushed this. A few notes:

* Thomas' patch for improving timeout.c seems like a great idea, but
it did indeed have a race condition, and I felt the comments could do
with more work.

* I'm not entirely comfortable with the name "idle_session_timeout",
because it sounds like it applies to all idle states, but actually
it only applies when we're not in a transaction. I left the name
alone and tried to clarify the docs, but I wonder whether a different
name would be better. (Alternatively, we could say that it does
apply in all cases, making the effective timeout when in a transaction
the smaller of the two GUCs. But that'd be more complex to implement
and less flexible, so I'm not in favor of that answer.)

* The SQLSTATE you chose for the new error condition seems pretty
random. I do not see it in the SQL standard, so using a code that's
within the spec-reserved code range is certainly wrong. I went with
08P02 (the "P" makes it outside the reserved range), but I'm not
really happy either with using class 08 ("Connection Exception",
which seems to be mainly meant for connection-request failures),
or the fact that ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT is
practically identical but it's not even in the same major class.
Now 25 ("Invalid Transaction State") is certainly not right for
this new error, but I think what that's telling us is that 25 was a
misguided choice for the other error. In a green field I think I'd
put both of them in class 53 ("Insufficient Resources") or maybe class
57 ("Operator Intervention"). Can we get away with renumbering the
older error at this point? In any case I'd be inclined to move the
new error to 53 or 57 --- anybody have a preference which?

* I think the original intent in timeout.h was to have 10 slots
available for run-time-defined timeout reasons. This is the third
predefined one we've added since the header was created, so it's
starting to look a little tight. I adjusted the code to hopefully
preserve 10 free slots going forward.

* I noted the discussion about dropping setitimer in place of some
newer kernel API. I'm not sure that that is worth the trouble in
isolation, but it might be worth doing if we can switch the whole
module over to relying on CLOCK_MONOTONIC, so as to make its behavior
less flaky if the sysadmin steps the system clock. Portability
might be a big issue here though, plus we'd have to figure out how
we want to define enable_timeout_at(), which is unlikely to want to
use CLOCK_MONOTONIC values. In any case, that's surely material
for a whole new thread.

regards, tom lane

#46Thomas Munro
thomas.munro@gmail.com
In reply to: Tom Lane (#45)
Re: Terminate the idle sessions

On Thu, Jan 7, 2021 at 12:55 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

* Thomas' patch for improving timeout.c seems like a great idea, but
it did indeed have a race condition, and I felt the comments could do
with more work.

Oops, and thanks! Very happy to see this one in the tree.

* I'm not entirely comfortable with the name "idle_session_timeout",
because it sounds like it applies to all idle states, but actually
it only applies when we're not in a transaction. I left the name
alone and tried to clarify the docs, but I wonder whether a different
name would be better. (Alternatively, we could say that it does
apply in all cases, making the effective timeout when in a transaction
the smaller of the two GUCs. But that'd be more complex to implement
and less flexible, so I'm not in favor of that answer.)

Hmm, it is a bit confusing, but having them separate is indeed more flexible.

* The SQLSTATE you chose for the new error condition seems pretty
random. I do not see it in the SQL standard, so using a code that's
within the spec-reserved code range is certainly wrong. I went with
08P02 (the "P" makes it outside the reserved range), but I'm not
really happy either with using class 08 ("Connection Exception",
which seems to be mainly meant for connection-request failures),
or the fact that ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT is
practically identical but it's not even in the same major class.
Now 25 ("Invalid Transaction State") is certainly not right for
this new error, but I think what that's telling us is that 25 was a
misguided choice for the other error. In a green field I think I'd
put both of them in class 53 ("Insufficient Resources") or maybe class
57 ("Operator Intervention"). Can we get away with renumbering the
older error at this point? In any case I'd be inclined to move the
new error to 53 or 57 --- anybody have a preference which?

I don't have a strong view here, but 08 with a P doesn't seem crazy to
me. Unlike eg 57014 (statement_timeout), 57017 (deadlock_timeout),
55P03 (lock_timeout... huh, I just noticed that DB2 uses 57017 for
that, distinguished from deadlock by another error field), after these
timeouts you don't have a session/connection anymore. The two are a
bit different though: in the older one, you were in a transaction, and
it seems to me quite newsworthy that your transaction has been
aborted, information that is not conveyed quite so clearly with a
connection-related error class.

* I noted the discussion about dropping setitimer in place of some
newer kernel API. I'm not sure that that is worth the trouble in
isolation, but it might be worth doing if we can switch the whole
module over to relying on CLOCK_MONOTONIC, so as to make its behavior
less flaky if the sysadmin steps the system clock. Portability
might be a big issue here though, plus we'd have to figure out how
we want to define enable_timeout_at(), which is unlikely to want to
use CLOCK_MONOTONIC values. In any case, that's surely material
for a whole new thread.

+1

#47Thomas Munro
thomas.munro@gmail.com
In reply to: Thomas Munro (#46)
Re: Terminate the idle sessions

On Thu, Jan 7, 2021 at 3:03 PM Thomas Munro <thomas.munro@gmail.com> wrote:

On Thu, Jan 7, 2021 at 12:55 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

* The SQLSTATE you chose for the new error condition seems pretty
random. I do not see it in the SQL standard, so using a code that's
within the spec-reserved code range is certainly wrong. I went with
08P02 (the "P" makes it outside the reserved range), but I'm not
really happy either with using class 08 ("Connection Exception",
which seems to be mainly meant for connection-request failures),
or the fact that ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT is
practically identical but it's not even in the same major class.
Now 25 ("Invalid Transaction State") is certainly not right for
this new error, but I think what that's telling us is that 25 was a
misguided choice for the other error. In a green field I think I'd
put both of them in class 53 ("Insufficient Resources") or maybe class
57 ("Operator Intervention"). Can we get away with renumbering the
older error at this point? In any case I'd be inclined to move the
new error to 53 or 57 --- anybody have a preference which?

I don't have a strong view here, but 08 with a P doesn't seem crazy to
me. Unlike eg 57014 (statement_timeout), 57017 (deadlock_timeout),
55P03 (lock_timeout... huh, I just noticed that DB2 uses 57017 for
that, distinguished from deadlock by another error field), after these
timeouts you don't have a session/connection anymore. The two are a
bit different though: in the older one, you were in a transaction, and
it seems to me quite newsworthy that your transaction has been
aborted, information that is not conveyed quite so clearly with a
connection-related error class.

Hmm, on further reflection it's still more similar than different and
I'd probably have voted for 08xxx for the older one too if I'd been
paying attention.

One of the strange things about these errors is that they're
asynchronous/unsolicited, but they appear to the client to be the
response to their next request (if it doesn't eat ECONNRESET instead).
That makes me think we should try to make it clear that it's a sort of
lower level thing, and not really a response to your next request at
all. Perhaps 08 does that. But it's not obvious... I see a couple
of DB2 extension SQLSTATEs saying you have no connection: 57015 =
"Connection to the local Db2 not established" and 51006 = "A valid
connection has not been established", and then there's standard 08003
= "connection does not exist" which we're currently using to shout
into the void when the *client* goes away (and also for dblink failure
to find named connection, a pretty unrelated meaning).

#48Tom Lane
tgl@sss.pgh.pa.us
In reply to: Thomas Munro (#47)
Re: Terminate the idle sessions

Thomas Munro <thomas.munro@gmail.com> writes:

One of the strange things about these errors is that they're
asynchronous/unsolicited, but they appear to the client to be the
response to their next request (if it doesn't eat ECONNRESET instead).

Right, which is what makes class 57 (operator intervention) seem
attractive to me. From the client's standpoint these look little
different from ERRCODE_ADMIN_SHUTDOWN or ERRCODE_CRASH_SHUTDOWN,
which are in that category.

regards, tom lane

#49Li Japin
japinli@hotmail.com
In reply to: Thomas Munro (#46)
Re: Terminate the idle sessions

--
Best regards
Japin Li

On Jan 7, 2021, at 10:03 AM, Thomas Munro <thomas.munro@gmail.com<mailto:thomas.munro@gmail.com>> wrote:

* I'm not entirely comfortable with the name "idle_session_timeout",
because it sounds like it applies to all idle states, but actually
it only applies when we're not in a transaction. I left the name
alone and tried to clarify the docs, but I wonder whether a different
name would be better. (Alternatively, we could say that it does
apply in all cases, making the effective timeout when in a transaction
the smaller of the two GUCs. But that'd be more complex to implement
and less flexible, so I'm not in favor of that answer.)

Hmm, it is a bit confusing, but having them separate is indeed more flexible.

Yes! It is a bit confusing. How about interactive_timeout? This is used by MySQL [1]https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_interactive_timeout.

* The SQLSTATE you chose for the new error condition seems pretty
random. I do not see it in the SQL standard, so using a code that's
within the spec-reserved code range is certainly wrong. I went with
08P02 (the "P" makes it outside the reserved range), but I'm not
really happy either with using class 08 ("Connection Exception",
which seems to be mainly meant for connection-request failures),
or the fact that ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT is
practically identical but it's not even in the same major class.
Now 25 ("Invalid Transaction State") is certainly not right for
this new error, but I think what that's telling us is that 25 was a
misguided choice for the other error. In a green field I think I'd
put both of them in class 53 ("Insufficient Resources") or maybe class
57 ("Operator Intervention"). Can we get away with renumbering the
older error at this point? In any case I'd be inclined to move the
new error to 53 or 57 --- anybody have a preference which?

I don't have a strong view here, but 08 with a P doesn't seem crazy to
me. Unlike eg 57014 (statement_timeout), 57017 (deadlock_timeout),
55P03 (lock_timeout... huh, I just noticed that DB2 uses 57017 for
that, distinguished from deadlock by another error field), after these
timeouts you don't have a session/connection anymore. The two are a
bit different though: in the older one, you were in a transaction, and
it seems to me quite newsworthy that your transaction has been
aborted, information that is not conveyed quite so clearly with a
connection-related error class.

Apologize! I just think it is a Connection Exception.

[1]: https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_interactive_timeout

#50Thomas Munro
thomas.munro@gmail.com
In reply to: Tom Lane (#48)
Re: Terminate the idle sessions

On Thu, Jan 7, 2021 at 4:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Thomas Munro <thomas.munro@gmail.com> writes:

One of the strange things about these errors is that they're
asynchronous/unsolicited, but they appear to the client to be the
response to their next request (if it doesn't eat ECONNRESET instead).

Right, which is what makes class 57 (operator intervention) seem
attractive to me. From the client's standpoint these look little
different from ERRCODE_ADMIN_SHUTDOWN or ERRCODE_CRASH_SHUTDOWN,
which are in that category.

Yeah, that's a good argument.

#51Tom Lane
tgl@sss.pgh.pa.us
In reply to: Thomas Munro (#50)
Re: Terminate the idle sessions

Thomas Munro <thomas.munro@gmail.com> writes:

On Thu, Jan 7, 2021 at 4:51 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

Thomas Munro <thomas.munro@gmail.com> writes:

One of the strange things about these errors is that they're
asynchronous/unsolicited, but they appear to the client to be the
response to their next request (if it doesn't eat ECONNRESET instead).

Right, which is what makes class 57 (operator intervention) seem
attractive to me. From the client's standpoint these look little
different from ERRCODE_ADMIN_SHUTDOWN or ERRCODE_CRASH_SHUTDOWN,
which are in that category.

Yeah, that's a good argument.

Given the lack of commentary on this thread, I'm guessing that people
aren't so excited about this topic that a change in the existing sqlstate
assignment for ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT would fly.
So I propose to change the new ERRCODE_IDLE_SESSION_TIMEOUT to be in
class 57 and call it good.

regards, tom lane

#52kuroda.hayato@fujitsu.com
kuroda.hayato@fujitsu.com
In reply to: Tom Lane (#51)
RE: Terminate the idle sessions

Dear Tom,

So I propose to change the new ERRCODE_IDLE_SESSION_TIMEOUT to be in
class 57 and call it good.

I agreed your suggestion and I confirmed your commit.
Thanks!

Hayato Kuroda
FUJITSU LIMITED