diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index d383de2512..211f77513d 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -939,6 +939,30 @@ include_dir 'conf.d' + + tcp_user_timeout (integer) + + tcp_user_timeout configuration parameter + + + + + Specifies the number of milliseconds that transmitted data may + remain unacknowledged before a connection is forcefully closed. + A value of 0 uses the system default. + This parameter is supported only on systems that support + TCP_USER_TIMEOUT; on other systems, it must be zero. + In sessions connected via a Unix-domain socket, + this parameter is ignored and always reads as zero. + + + + This parameter is not supported on Windows and Linux version 2.6.36 or earlier. + + + + + diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index c39617a430..1addeeef58 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,56 @@ pq_setkeepalivescount(int count, Port *port) return STATUS_OK; } + +int +pq_gettcpusertimeout(Port *port) +{ +#ifdef TCP_USER_TIMEOUT + if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family)) + return 0; + + if (port->tcp_user_timeout != 0) + return port->tcp_user_timeout; + + if (port->default_tcp_user_timeout == 0) + { + ACCEPT_TYPE_ARG3 size = sizeof(port->default_tcp_user_timeout); + + if (getsockopt(port->sock, IPPROTO_TCP, TCP_USER_TIMEOUT, + (char *) &port->default_tcp_user_timeout, + &size) < 0) + { + elog(LOG, "getsockopt(%s) failed: %m", "TCP_USER_TIMEOUT"); + port->default_tcp_user_timeout = -1; /* don't know */ + } + } + + return port->default_tcp_user_timeout; +#else + return 0; +#endif +} + +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, TCP_USER_TIMEOUT, + (char *) &timeout, sizeof(timeout)) < 0) + { + elog(LOG, "setsockopt(%s) failed: %m", "TCP_USER_TIMEOUT"); + return STATUS_ERROR; + } +#else + if (timeout != 0) + { + elog(LOG, "setsockopt(%s) not supported", "TCP_USER_TIMEOUT"); + return STATUS_ERROR; + } +#endif + + return STATUS_OK; +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index aa564d153a..ca3f8dfd0e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -182,9 +182,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); @@ -529,6 +531,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 @@ -3132,6 +3135,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 @@ -11047,6 +11061,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", pq_gettcpusertimeout(MyProcPort)); + return nbuf; +} + static bool check_maxconnections(int *newval, void **extra, GucSource source) { diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index cccb5f145a..ce5b66ab26 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -83,6 +83,10 @@ #tcp_keepalives_count = 0 # TCP_KEEPCNT; # 0 selects the system default +# - TCP USER TIMEOUT - +#tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; + # 0 selects the system default + # - Authentication - #authentication_timeout = 1min # 1s-600s diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 248055f10b..dffc58ef7d 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -164,6 +164,17 @@ typedef struct Port int keepalives_interval; int keepalives_count; + /* + * TCP USER TIMEOUT setting. + * + * default values are 0 if AF_UNIX or not yet known; current values are 0 + * if AF_UNIX or using the default. Also, -1 in a default value means we + * were unable to find out the default (getsockopt failed). + */ + int default_tcp_user_timeout; + int tcp_user_timeout; + + #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) /* @@ -286,9 +297,11 @@ extern ProtocolVersion FrontendProtocol; extern int pq_getkeepalivesidle(Port *port); extern int pq_getkeepalivesinterval(Port *port); extern int pq_getkeepalivescount(Port *port); +extern int pq_gettcpusertimeout(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 2712a774f7..0773644f57 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;