Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 249d649

Browse files
committedApr 6, 2019
Add support TCP user timeout in libpq and the backend server
Similarly to the set of parameters for keepalive, a connection parameter for libpq is added as well as a backend GUC, called tcp_user_timeout. Increasing the TCP user timeout is useful to allow a connection to survive extended periods without end-to-end connection, and decreasing it allows application to fail faster. By default, the parameter is 0, which makes the connection use the system default, and follows a logic close to the keepalive parameters in its handling. When connecting through a Unix-socket domain, the parameters have no effect. Author: Ryohei Nagaura Reviewed-by: Fabien Coelho, Robert Haas, Kyotaro Horiguchi, Kirk Jamison, Mikalai Keida, Takayuki Tsunakawa, Andrei Yahorau Discussion: https://postgr.es/m/EDA4195584F5064680D8130B1CA91C45367328@G01JPEXMBYT04
1 parent 959d00e commit 249d649

File tree

11 files changed

+198
-2
lines changed

11 files changed

+198
-2
lines changed
 

‎contrib/postgres_fdw/expected/postgres_fdw.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ ALTER SERVER testserver1 OPTIONS (
151151
keepalives 'value',
152152
keepalives_idle 'value',
153153
keepalives_interval 'value',
154+
tcp_user_timeout 'value',
154155
-- requiressl 'value',
155156
sslcompression 'value',
156157
sslmode 'value',

‎contrib/postgres_fdw/sql/postgres_fdw.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ ALTER SERVER testserver1 OPTIONS (
164164
keepalives 'value',
165165
keepalives_idle 'value',
166166
keepalives_interval 'value',
167+
tcp_user_timeout 'value',
167168
-- requiressl 'value',
168169
sslcompression 'value',
169170
sslmode 'value',

‎doc/src/sgml/config.sgml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,31 @@ include_dir 'conf.d'
939939
</listitem>
940940
</varlistentry>
941941

942+
<varlistentry id="guc-tcp-user-timeout" xreflabel="tcp_user_timeout">
943+
<term><varname>tcp_user_timeout</varname> (<type>integer</type>)
944+
<indexterm>
945+
<primary><varname>tcp_user_timeout</varname> configuration parameter</primary>
946+
</indexterm>
947+
</term>
948+
<listitem>
949+
<para>
950+
Specifies the number of milliseconds that transmitted data may
951+
remain unacknowledged before a connection is forcibly closed.
952+
A value of 0 uses the system default.
953+
This parameter is supported only on systems that support
954+
<symbol>TCP_USER_TIMEOUT</symbol>; on other systems, it must be zero.
955+
In sessions connected via a Unix-domain socket, this parameter is
956+
ignored and always reads as zero.
957+
</para>
958+
<note>
959+
<para>
960+
This parameter is not supported on Windows and on Linux version
961+
2.6.36 or older.
962+
</para>
963+
</note>
964+
</listitem>
965+
</varlistentry>
966+
942967
</variablelist>
943968
</sect2>
944969

‎doc/src/sgml/libpq.sgml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,20 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
12491249
</listitem>
12501250
</varlistentry>
12511251

1252+
<varlistentry id="libpq-tcp-user-timeout" xreflabel="libpq_tcp_user_timeout">
1253+
<term><literal>tcp_user_timeout</literal></term>
1254+
<listitem>
1255+
<para>
1256+
Controls the number of milliseconds that transmitted data may
1257+
remain unacknowledged before a connection is forcibly closed.
1258+
A value of zero uses the system default. This parameter is
1259+
ignored for connections made via a Unix-domain socket.
1260+
It is only supported on systems where <symbol>TCP_USER_TIMEOUT</symbol>
1261+
is available; on other systems, it has no effect.
1262+
</para>
1263+
</listitem>
1264+
</varlistentry>
1265+
12521266
<varlistentry id="libpq-connect-tty" xreflabel="tty">
12531267
<term><literal>tty</literal></term>
12541268
<listitem>

‎src/backend/libpq/pqcomm.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ StreamConnection(pgsocket server_fd, Port *port)
825825
(void) pq_setkeepalivesidle(tcp_keepalives_idle, port);
826826
(void) pq_setkeepalivesinterval(tcp_keepalives_interval, port);
827827
(void) pq_setkeepalivescount(tcp_keepalives_count, port);
828+
(void) pq_settcpusertimeout(tcp_user_timeout, port);
828829
}
829830

830831
return STATUS_OK;
@@ -1926,3 +1927,75 @@ pq_setkeepalivescount(int count, Port *port)
19261927

19271928
return STATUS_OK;
19281929
}
1930+
1931+
int
1932+
pq_gettcpusertimeout(Port *port)
1933+
{
1934+
#ifdef TCP_USER_TIMEOUT
1935+
if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
1936+
return 0;
1937+
1938+
if (port->tcp_user_timeout != 0)
1939+
return port->tcp_user_timeout;
1940+
1941+
if (port->default_tcp_user_timeout == 0)
1942+
{
1943+
ACCEPT_TYPE_ARG3 size = sizeof(port->default_tcp_user_timeout);
1944+
1945+
if (getsockopt(port->sock, IPPROTO_TCP, TCP_USER_TIMEOUT,
1946+
(char *) &port->default_tcp_user_timeout,
1947+
&size) < 0)
1948+
{
1949+
elog(LOG, "getsockopt(%s) failed: %m", "TCP_USER_TIMEOUT");
1950+
port->default_tcp_user_timeout = -1; /* don't know */
1951+
}
1952+
}
1953+
1954+
return port->default_tcp_user_timeout;
1955+
#else
1956+
return 0;
1957+
#endif
1958+
}
1959+
1960+
int
1961+
pq_settcpusertimeout(int timeout, Port *port)
1962+
{
1963+
if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
1964+
return STATUS_OK;
1965+
1966+
#ifdef TCP_USER_TIMEOUT
1967+
if (timeout == port->tcp_user_timeout)
1968+
return STATUS_OK;
1969+
1970+
if (port->default_tcp_user_timeout <= 0)
1971+
{
1972+
if (pq_gettcpusertimeout(port) < 0)
1973+
{
1974+
if (timeout == 0)
1975+
return STATUS_OK; /* default is set but unknown */
1976+
else
1977+
return STATUS_ERROR;
1978+
}
1979+
}
1980+
1981+
if (timeout == 0)
1982+
timeout = port->default_tcp_user_timeout;
1983+
1984+
if (setsockopt(port->sock, IPPROTO_TCP, TCP_USER_TIMEOUT,
1985+
(char *) &timeout, sizeof(timeout)) < 0)
1986+
{
1987+
elog(LOG, "setsockopt(%s) failed: %m", "TCP_USER_TIMEOUT");
1988+
return STATUS_ERROR;
1989+
}
1990+
1991+
port->tcp_user_timeout = timeout;
1992+
#else
1993+
if (timeout != 0)
1994+
{
1995+
elog(LOG, "setsockopt(%s) not supported", "TCP_USER_TIMEOUT");
1996+
return STATUS_ERROR;
1997+
}
1998+
#endif
1999+
2000+
return STATUS_OK;
2001+
}

‎src/backend/utils/misc/guc.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,11 @@ static const char *show_archive_command(void);
182182
static void assign_tcp_keepalives_idle(int newval, void *extra);
183183
static void assign_tcp_keepalives_interval(int newval, void *extra);
184184
static void assign_tcp_keepalives_count(int newval, void *extra);
185+
static void assign_tcp_user_timeout(int newval, void *extra);
185186
static const char *show_tcp_keepalives_idle(void);
186187
static const char *show_tcp_keepalives_interval(void);
187188
static const char *show_tcp_keepalives_count(void);
189+
static const char *show_tcp_user_timeout(void);
188190
static bool check_maxconnections(int *newval, void **extra, GucSource source);
189191
static bool check_max_worker_processes(int *newval, void **extra, GucSource source);
190192
static bool check_autovacuum_max_workers(int *newval, void **extra, GucSource source);
@@ -530,6 +532,7 @@ char *application_name;
530532
int tcp_keepalives_idle;
531533
int tcp_keepalives_interval;
532534
int tcp_keepalives_count;
535+
int tcp_user_timeout;
533536

534537
/*
535538
* SSL renegotiation was been removed in PostgreSQL 9.5, but we tolerate it
@@ -3182,6 +3185,17 @@ static struct config_int ConfigureNamesInt[] =
31823185
NULL, NULL, NULL
31833186
},
31843187

3188+
{
3189+
{"tcp_user_timeout", PGC_USERSET, CLIENT_CONN_OTHER,
3190+
gettext_noop("TCP user timeout."),
3191+
gettext_noop("A value of 0 uses the system default."),
3192+
GUC_UNIT_MS
3193+
},
3194+
&tcp_user_timeout,
3195+
0, 0, INT_MAX,
3196+
NULL, assign_tcp_user_timeout, show_tcp_user_timeout
3197+
},
3198+
31853199
/* End-of-list marker */
31863200
{
31873201
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
@@ -11238,6 +11252,23 @@ show_tcp_keepalives_count(void)
1123811252
return nbuf;
1123911253
}
1124011254

11255+
static void
11256+
assign_tcp_user_timeout(int newval, void *extra)
11257+
{
11258+
/* See comments in assign_tcp_keepalives_idle */
11259+
(void) pq_settcpusertimeout(newval, MyProcPort);
11260+
}
11261+
11262+
static const char *
11263+
show_tcp_user_timeout(void)
11264+
{
11265+
/* See comments in assign_tcp_keepalives_idle */
11266+
static char nbuf[16];
11267+
11268+
snprintf(nbuf, sizeof(nbuf), "%d", pq_gettcpusertimeout(MyProcPort));
11269+
return nbuf;
11270+
}
11271+
1124111272
static bool
1124211273
check_maxconnections(int *newval, void **extra, GucSource source)
1124311274
{

‎src/backend/utils/misc/postgresql.conf.sample

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
#bonjour_name = '' # defaults to the computer name
7474
# (change requires restart)
7575

76-
# - TCP Keepalives -
76+
# - TCP settings -
7777
# see "man 7 tcp" for details
7878

7979
#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
@@ -82,6 +82,8 @@
8282
# 0 selects the system default
8383
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
8484
# 0 selects the system default
85+
#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
86+
# 0 selects the system default
8587

8688
# - Authentication -
8789

‎src/include/libpq/libpq-be.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ typedef struct Port
155155
HbaLine *hba;
156156

157157
/*
158-
* TCP keepalive settings.
158+
* TCP keepalive and user timeout settings.
159159
*
160160
* default values are 0 if AF_UNIX or not yet known; current values are 0
161161
* if AF_UNIX or using the default. Also, -1 in a default value means we
@@ -164,9 +164,11 @@ typedef struct Port
164164
int default_keepalives_idle;
165165
int default_keepalives_interval;
166166
int default_keepalives_count;
167+
int default_tcp_user_timeout;
167168
int keepalives_idle;
168169
int keepalives_interval;
169170
int keepalives_count;
171+
int tcp_user_timeout;
170172

171173
/*
172174
* GSSAPI structures.
@@ -306,9 +308,11 @@ extern ProtocolVersion FrontendProtocol;
306308
extern int pq_getkeepalivesidle(Port *port);
307309
extern int pq_getkeepalivesinterval(Port *port);
308310
extern int pq_getkeepalivescount(Port *port);
311+
extern int pq_gettcpusertimeout(Port *port);
309312

310313
extern int pq_setkeepalivesidle(int idle, Port *port);
311314
extern int pq_setkeepalivesinterval(int interval, Port *port);
312315
extern int pq_setkeepalivescount(int count, Port *port);
316+
extern int pq_settcpusertimeout(int timeout, Port *port);
313317

314318
#endif /* LIBPQ_BE_H */

‎src/include/utils/guc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ extern PGDLLIMPORT char *application_name;
271271
extern int tcp_keepalives_idle;
272272
extern int tcp_keepalives_interval;
273273
extern int tcp_keepalives_count;
274+
extern int tcp_user_timeout;
274275

275276
#ifdef TRACE_SORT
276277
extern bool trace_sort;

‎src/interfaces/libpq/fe-connect.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
270270
"TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
271271
offsetof(struct pg_conn, keepalives_count)},
272272

273+
{"tcp_user_timeout", NULL, NULL, NULL,
274+
"TCP-User-Timeout", "", 10, /* strlen(INT32_MAX) == 10 */
275+
offsetof(struct pg_conn, pgtcp_user_timeout)},
276+
273277
/*
274278
* ssl options are allowed even without client SSL support because the
275279
* client can still handle SSL modes "disable" and "allow". Other
@@ -1833,6 +1837,41 @@ setKeepalivesWin32(PGconn *conn)
18331837
#endif /* SIO_KEEPALIVE_VALS */
18341838
#endif /* WIN32 */
18351839

1840+
/*
1841+
* Set the TCP user timeout.
1842+
*/
1843+
static int
1844+
setTCPUserTimeout(PGconn *conn)
1845+
{
1846+
int timeout;
1847+
1848+
if (conn->pgtcp_user_timeout == NULL)
1849+
return 1;
1850+
1851+
if (!parse_int_param(conn->pgtcp_user_timeout, &timeout, conn,
1852+
"tcp_user_timeout"))
1853+
return 0;
1854+
1855+
if (timeout < 0)
1856+
timeout = 0;
1857+
1858+
#ifdef TCP_USER_TIMEOUT
1859+
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_USER_TIMEOUT,
1860+
(char *) &timeout, sizeof(timeout)) < 0)
1861+
{
1862+
char sebuf[256];
1863+
1864+
appendPQExpBuffer(&conn->errorMessage,
1865+
libpq_gettext("setsockopt(%s) failed: %s\n"),
1866+
"TCP_USER_TIMEOUT",
1867+
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
1868+
return 0;
1869+
}
1870+
#endif
1871+
1872+
return 1;
1873+
}
1874+
18361875
/* ----------
18371876
* connectDBStart -
18381877
* Begin the process of making a connection to the backend.
@@ -2480,6 +2519,8 @@ PQconnectPoll(PGconn *conn)
24802519
err = 1;
24812520
#endif /* SIO_KEEPALIVE_VALS */
24822521
#endif /* WIN32 */
2522+
else if (!setTCPUserTimeout(conn))
2523+
err = 1;
24832524

24842525
if (err)
24852526
{
@@ -3863,6 +3904,8 @@ freePGconn(PGconn *conn)
38633904
free(conn->pgtty);
38643905
if (conn->connect_timeout)
38653906
free(conn->connect_timeout);
3907+
if (conn->pgtcp_user_timeout)
3908+
free(conn->pgtcp_user_timeout);
38663909
if (conn->pgoptions)
38673910
free(conn->pgoptions);
38683911
if (conn->appname)

‎src/interfaces/libpq/libpq-int.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ struct pg_conn
336336
char *pgtty; /* tty on which the backend messages is
337337
* displayed (OBSOLETE, NOT USED) */
338338
char *connect_timeout; /* connection timeout (numeric string) */
339+
char *pgtcp_user_timeout; /* tcp user timeout (numeric string) */
339340
char *client_encoding_initial; /* encoding to use */
340341
char *pgoptions; /* options to start the backend with */
341342
char *appname; /* application name */

0 commit comments

Comments
 (0)
Failed to load comments.