inted RE: Timeout parameters
Hi, kirk-san.
From: Nagaura, Ryohei <nagaura.ryohei@jp.fujitsu.com>
This is my bad. I'll remake it.
Very sorry for the same mistake.
I remade the patches and attached in this mail.
In socket_timeout patch, I replaced "atoi" to "parse_int_param" and inserted spaces just after some comma.
There are a few changes about documentation for the following reason:
From: Jamison, Kirk <k.jamison@jp.fujitsu.com>
Got the doc fix. I wonder if we need to document what effect the parameter does:
terminating the connection. How about:
I also don't know, but...
Controls the number of seconds of client-server communication inactivity before
forcibly closing the connection in order to prevent client from infinite waiting for
individual socket read/write operations. This can be used both as a force global
query timeout and network problems detector, i.e. hardware failure and dead
connection. A value of zero (the default) turns this off.
"communication inactivity" seems to be a little extreme.
If the communication layer is truly dead you will use keepalive.
This use case is when socket option is not available for some reason.
So it would be better "terminating the connection" in my thought.
And...
Well, you may remove the "i.e. hardware failure and dead connection" if that's not
necessary.
I don't think it is necessary because you can use this parameter other than that situations.
Not "i.e." but "e.g." have a few chance to be documented.
About TCP_USER_TIMEOUT patches, there are only miscellaneous changes: removing trailing spaces
and making comments of parameters lower case as you pointed out.
Best regards,
---------------------
Ryohei Nagaura
Attachments:
socket_timeout_v6.patchapplication/octet-stream; name=socket_timeout_v6.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c1d1b6b2db..7b070263ff 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1249,6 +1249,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
+ <varlistentry id="socket_timeout" xreflabel="socket_timeout">
+ <term><literal>socket_timeout</literal></term>
+ <listitem>
+ <para>
+ Controls the number of second of client's waiting time for individual
+ socket read/write operations. If the specified time passed, the connectoin
+ would be terminated. This can be used both as a force
+ global query timeout and network problems detector.
+ A value of zero (the default) turns this off.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-tty" xreflabel="tty">
<term><literal>tty</literal></term>
<listitem>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c96a52bb1b..9045da5e4e 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -208,6 +208,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
offsetof(struct pg_conn, connect_timeout)},
+ {"socket_timeout", NULL, NULL, NULL,
+ "Socket-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
+ offsetof(struct pg_conn, pgsocket_timeout)},
+
{"dbname", "PGDATABASE", NULL, NULL,
"Database-Name", "", 20,
offsetof(struct pg_conn, dbName)},
@@ -405,6 +409,8 @@ static char *passwordFromFile(const char *hostname, const char *port, const char
const char *username, const char *pgpassfile);
static void pgpassfileWarning(PGconn *conn);
static void default_threadlock(int acquire);
+static bool parse_int_param(const char *value, int *result, PGconn *conn,
+ const char *context);
/* global variable because fe-auth.c needs to access it */
@@ -1234,6 +1240,19 @@ connectOptions2(PGconn *conn)
goto oom_error;
}
+ if (conn->pgsocket_timeout)
+ {
+ if (parse_int_param(conn->pgsocket_timeout,
+ &conn->socket_timeout, conn, "socket_timeout"))
+ {
+ /*
+ * Rounding could cause communication to fail; need at least 2 secs
+ */
+ if(conn->socket_timeout > 0 && conn->socket_timeout < 2)
+ conn->socket_timeout = 2;
+ }
+ }
+
/*
* Validate target_session_attrs option.
*/
@@ -3651,6 +3670,8 @@ freePGconn(PGconn *conn)
free(conn->pgtty);
if (conn->connect_timeout)
free(conn->connect_timeout);
+ if (conn->pgsocket_timeout)
+ free(conn->pgsocket_timeout);
if (conn->pgoptions)
free(conn->pgoptions);
if (conn->appname)
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index e5ef8d44bd..59f047e732 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -983,6 +983,33 @@ pqFlush(PGconn *conn)
int
pqWait(int forRead, int forWrite, PGconn *conn)
{
+ int result;
+
+ if (conn->socket_timeout > 0)
+ {
+ time_t finish_time;
+
+ /* calculate the finish time based on start + timeout */
+ finish_time = time(NULL) + conn->socket_timeout;
+ result = pqSocketCheck(conn, forRead, forWrite, finish_time);
+
+ if (result < 0)
+ return EOF;
+
+ if (result == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("timeout expired\n"));
+ conn->status = CONNECTION_BAD;
+ pqsecure_close(conn);
+ closesocket(conn->sock);
+ conn->sock = -1;
+ return EOF;
+ }
+
+ return 0;
+ }
+
return pqWaitTimed(forRead, forWrite, conn, (time_t) -1);
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a93d8edbc..816ebda4dc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -336,6 +336,7 @@ struct pg_conn
char *pgtty; /* tty on which the backend messages is
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
+ char *pgsocket_timeout; /* socket timeout (numeric string) */
char *client_encoding_initial; /* encoding to use */
char *pgoptions; /* options to start the backend with */
char *appname; /* application name */
@@ -500,6 +501,7 @@ struct pg_conn
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
+ int socket_timeout; /* socket timeout (numeric)*/
};
/* PGcancel stores all data necessary to cancel a connection. A copy of this
TCP_backend_v6.patchapplication/octet-stream; name=TCP_backend_v6.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 8bd57f376b..cb509a710a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -923,6 +923,49 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-tcp-user-timeout" xreflabel="tcp_user_timeout">
+ <term><varname>tcp_user_timeout</varname> (<type>integer</type>)
+ <indexterm>
+ <primary><varname>tcp_user_timeout</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Define a wrapper for TCP_USER_TIMEOUT socket option of libpq connection.
+ </para>
+ <para>
+ Specifies the number of milliseconds after which a TCP connection can be
+ aborted by the operation system due to network problems when the data is
+ transmitting through this connection (sending/receiving). A value of 0 uses
+ the system default. This parameter is supported only on systems that support
+ TCP_USER_TIMEOUT or an equivalent socket option, and on Windows; on other
+ systems, it must be zero. In sessions connected via a Unix-domain socket,
+ this parameter is ignored and always reads as zero.
+ </para>
+ <note>
+ <para>
+ This parameter is not supported on Windows, and must be zero.
+ </para>
+ <para>
+ To enable full control under TCP connection use this option together with
+ keepalive.
+ </para>
+ </note>
+ </listitem>
+ <listitem>
+ <para>
+ Specify in milliseconds the time to disconnect to the client
+ when there is no ack packet from the client to the server's data transmission.
+ This parameter is supported on linux version 2.6.37 or later.
+ </para>
+ <note>
+ <para>
+ This parameter is not supported on Windows.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index c39617a430..b649237051 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -825,6 +825,7 @@ StreamConnection(pgsocket server_fd, Port *port)
(void) pq_setkeepalivesidle(tcp_keepalives_idle, port);
(void) pq_setkeepalivesinterval(tcp_keepalives_interval, port);
(void) pq_setkeepalivescount(tcp_keepalives_count, port);
+ (void) pq_settcpusertimeout(tcp_user_timeout, port);
}
return STATUS_OK;
@@ -1926,3 +1927,20 @@ pq_setkeepalivescount(int count, Port *port)
return STATUS_OK;
}
+
+int
+pq_settcpusertimeout(int timeout, Port *port)
+{
+#ifdef TCP_USER_TIMEOUT
+ if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
+ return STATUS_OK;
+
+ if (setsockopt(port->sock, IPPROTO_TCP, 18,
+ (char *) &timeout, sizeof(timeout)) << 0 && errno != ENOPROTOOPT)
+ {
+ elog(LOG, "setsockopt(TCP_USER_TIMEOUT) failed: %m");
+ return STATUS_ERROR;
+ }
+#endif
+ return STATUS_OK;
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 156d147c85..a3a96a3b16 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -181,9 +181,11 @@ static const char *show_archive_command(void);
static void assign_tcp_keepalives_idle(int newval, void *extra);
static void assign_tcp_keepalives_interval(int newval, void *extra);
static void assign_tcp_keepalives_count(int newval, void *extra);
+static void assign_tcp_user_timeout(int newval, void *extra);
static const char *show_tcp_keepalives_idle(void);
static const char *show_tcp_keepalives_interval(void);
static const char *show_tcp_keepalives_count(void);
+static const char *show_tcp_user_timeout(void);
static bool check_maxconnections(int *newval, void **extra, GucSource source);
static bool check_max_worker_processes(int *newval, void **extra, GucSource source);
static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
@@ -528,6 +530,7 @@ char *application_name;
int tcp_keepalives_idle;
int tcp_keepalives_interval;
int tcp_keepalives_count;
+int tcp_user_timeout;
/*
* SSL renegotiation was been removed in PostgreSQL 9.5, but we tolerate it
@@ -3157,6 +3160,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"tcp_user_timeout", PGC_USERSET, CLIENT_CONN_OTHER,
+ gettext_noop("TCP user timeout."),
+ gettext_noop("A value of 0 uses the system default."),
+ GUC_UNIT_MS | GUC_NOT_IN_SAMPLE
+ },
+ &tcp_user_timeout,
+ 12000, 0, INT_MAX,
+ NULL, assign_tcp_user_timeout, show_tcp_user_timeout
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
@@ -10908,6 +10922,23 @@ show_tcp_keepalives_count(void)
return nbuf;
}
+static void
+assign_tcp_user_timeout(int newval, void *extra)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+ (void) pq_settcpusertimeout(newval, MyProcPort);
+}
+
+static const char*
+show_tcp_user_timeout(void)
+{
+ /* See comments in assign_tcp_keepalives_idle */
+ static char nbuf[16];
+
+ snprintf(nbuf, sizeof(nbuf), "%d", tcp_user_timeout);
+ return nbuf;
+}
+
static bool
check_maxconnections(int *newval, void **extra, GucSource source)
{
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 248055f10b..2e8b2c34a9 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -290,5 +290,6 @@ extern int pq_getkeepalivescount(Port *port);
extern int pq_setkeepalivesidle(int idle, Port *port);
extern int pq_setkeepalivesinterval(int interval, Port *port);
extern int pq_setkeepalivescount(int count, Port *port);
+extern int pq_settcpusertimeout(int timeout, Port *port);
#endif /* LIBPQ_BE_H */
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c07e7b945e..0d023965b2 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -268,6 +268,7 @@ extern PGDLLIMPORT char *application_name;
extern int tcp_keepalives_idle;
extern int tcp_keepalives_interval;
extern int tcp_keepalives_count;
+extern int tcp_user_timeout;
#ifdef TRACE_SORT
extern bool trace_sort;
TCP_interface_v6.patchapplication/octet-stream; name=TCP_interface_v6.patchDownload
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c1d1b6b2db..ac4ac5e978 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1249,6 +1249,33 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
+ <varlistentry id="libpq-tcp-user-timeout" xreflabel="libpq_tcp_user_timeout">
+ <term><literal>tcp_user_timeout</literal></term>
+ <listitem>
+ <para>
+ Define a wrapper for TCP_USER_TIMEOUT socket option of libpq connection.
+ </para>
+ <para>
+ Specifies the number of milliseconds after which a TCP connection can be
+ aborted by the operation system due to network problems when the data is
+ transmitting through this connection (sending/receiving). A value of 0 uses
+ the system default. This parameter is supported only on systems that support
+ TCP_USER_TIMEOUT or an equivalent socket option, and on Windows; on other
+ systems, it must be zero. In sessions connected via a Unix-domain socket,
+ this parameter is ignored and always reads as zero.
+ </para>
+ <note>
+ <para>
+ This parameter is not supported on Windows, and must be zero.
+ </para>
+ <para>
+ To enable full control under TCP connection use this option together with
+ keepalive.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-tty" xreflabel="tty">
<term><literal>tty</literal></term>
<listitem>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c96a52bb1b..bc0baf820c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -325,6 +325,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
offsetof(struct pg_conn, target_session_attrs)},
+ /* TCP USER TIMEOUT */
+ {"tcp_user_timeout", NULL, NULL, NULL,
+ "TCP_user_timeout", "", 10, /* strlen(INT32_MAX) == 10 */
+ offsetof(struct pg_conn, pgtcp_user_timeout)},
+
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
NULL, NULL, 0}
@@ -1782,6 +1787,40 @@ setKeepalivesWin32(PGconn *conn)
#endif /* SIO_KEEPALIVE_VALS */
#endif /* WIN32 */
+/*
+ * Set the TCP user timeout.
+ */
+static int
+setTCPUserTimeout(PGconn *conn)
+{
+ int timeout;
+
+ if (conn->pgtcp_user_timeout == NULL)
+ return 1;
+
+ if (!parse_int_param(conn->pgtcp_user_timeout,
+ &timeout, conn, "tcp_user_timeout"))
+ return 0;
+
+ if (timeout < 0)
+ timeout = 0;
+
+#ifdef TCP_USER_TIMEOUT
+ if (setsockopt(conn->sock, IPPROTO_TCP, 18,
+ (char *) &timeout, sizeof(timeout)) < 0 && errno != ENOPROTOOPT)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(TCP_USER_TIMEOUT) failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
/* ----------
* connectDBStart -
* Begin the process of making a connection to the backend.
@@ -2373,6 +2412,17 @@ keep_going: /* We will come back to here until there is
goto keep_going;
}
+ if (!IS_AF_UNIX(addr_cur->ai_family))
+ {
+ if (!setTCPUserTimeout(conn))
+ {
+ closesocket(conn->sock);
+ conn->sock = -1;
+ conn->addr_cur = addr_cur->ai_next;
+ goto keep_going;
+ }
+ }
+
#ifdef F_SETFD
if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
{
@@ -3651,6 +3701,8 @@ freePGconn(PGconn *conn)
free(conn->pgtty);
if (conn->connect_timeout)
free(conn->connect_timeout);
+ if (conn->pgtcp_user_timeout)
+ free(conn->pgtcp_user_timeout);
if (conn->pgoptions)
free(conn->pgoptions);
if (conn->appname)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a93d8edbc..729342c4cc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -336,6 +336,7 @@ struct pg_conn
char *pgtty; /* tty on which the backend messages is
* displayed (OBSOLETE, NOT USED) */
char *connect_timeout; /* connection timeout (numeric string) */
+ char *pgtcp_user_timeout; /* tcp user timeout (numeric string) */
char *client_encoding_initial; /* encoding to use */
char *pgoptions; /* options to start the backend with */
char *appname; /* application name */
@@ -351,6 +352,7 @@ struct pg_conn
* retransmits */
char *keepalives_count; /* maximum number of TCP keepalive
* retransmits */
+ char *tcp_user_timeout; /* tcp user timeout */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
char *sslcompression; /* SSL compression (0 or 1) */
char *sslkey; /* client key filename */