From 1f54d43075d8c457935cd9fe8adfea949104995d Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 30 Jul 2005 15:17:26 +0000 Subject: [PATCH] Add GUC variables to control keep-alive times for idle, interval, and count. Oliver Jowett --- doc/src/sgml/runtime.sgml | 49 +++- src/backend/libpq/pqcomm.c | 213 +++++++++++++++++- src/backend/utils/misc/guc.c | 96 +++++++- src/backend/utils/misc/postgresql.conf.sample | 5 + src/bin/psql/tab-complete.c | 5 +- src/include/libpq/libpq-be.h | 33 ++- src/include/utils/guc.h | 5 +- 7 files changed, 399 insertions(+), 7 deletions(-) diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 4f24a6e876..41f2472f75 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ @@ -894,6 +894,53 @@ SET ENABLE_SEQSCAN TO OFF; + + tcp_keepalives_idle (integer) + + tcp_keepalives_idle configuration parameter + + + + On systems that support the TCP_KEEPIDLE socket option, specifies the + number of seconds between sending keepalives on an otherwise idle + connection. A value of 0 uses the system default. If TCP_KEEPIDLE is + not supported, this parameter must be 0. This option is ignored for + connections made via a Unix-domain socket. + + + + + + tcp_keepalives_interval (integer) + + tcp_keepalives_interval configuration parameter + + + + On systems that support the TCP_KEEPINTVL socket option, specifies how + long, in seconds, to wait for a response to a keepalive before + retransmitting. A value of 0 uses the system default. If TCP_KEEPINTVL + is not supported, this parameter must be 0. This option is ignored + for connections made via a Unix-domain socket. + + + + + + tcp_keepalives_count (integer) + + tcp_keepalives_count configuration parameter + + + + On systems that support the TCP_KEEPCNT socket option, specifies how + many keepalives may be lost before the connection is considered dead. + A value of 0 uses the system default. If TCP_KEEPINTVL is not + supported, this parameter must be 0. + + + + diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index ce321af4b1..43c2a88a45 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -30,7 +30,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.176 2005/02/22 04:35:57 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.177 2005/07/30 15:17:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -87,7 +87,7 @@ #include "libpq/libpq.h" #include "miscadmin.h" #include "storage/ipc.h" - +#include "utils/guc.h" /* * Configuration options @@ -594,6 +594,19 @@ StreamConnection(int server_fd, Port *port) elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m"); return STATUS_ERROR; } + + /* Set default keepalive parameters. This should also catch + * misconfigurations (non-zero values when socket options aren't + * supported) + */ + if (pq_setkeepalivesidle(tcp_keepalives_idle, port) != STATUS_OK) + return STATUS_ERROR; + + if (pq_setkeepalivesinterval(tcp_keepalives_interval, port) != STATUS_OK) + return STATUS_ERROR; + + if (pq_setkeepalivescount(tcp_keepalives_count, port) != STATUS_OK) + return STATUS_ERROR; } return STATUS_OK; @@ -1158,3 +1171,199 @@ pq_endcopyout(bool errorAbort) /* in non-error case, copy.c will have emitted the terminator line */ DoingCopyOut = false; } + +int +pq_getkeepalivesidle(Port *port) +{ +#ifdef TCP_KEEPIDLE + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return 0; + + if (port->keepalives_idle != 0) + return port->keepalives_idle; + + if (port->default_keepalives_idle == 0) + { + socklen_t size = sizeof(port->default_keepalives_idle); + if (getsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE, + (char *) &port->default_keepalives_idle, + &size) < 0) + { + elog(LOG, "getsockopt(TCP_KEEPIDLE) failed: %m"); + return -1; + } + } + + return port->default_keepalives_idle; +#else + return 0; +#endif +} + +int +pq_setkeepalivesidle(int idle, Port *port) +{ + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return STATUS_OK; + +#ifdef TCP_KEEPIDLE + if (idle == port->keepalives_idle) + return STATUS_OK; + + if (port->default_keepalives_idle == 0) + { + if (pq_getkeepalivesidle(port) < 0) + return STATUS_ERROR; + } + + if (idle == 0) + idle = port->default_keepalives_idle; + + if (setsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE, + (char *) &idle, sizeof(idle)) < 0) + { + elog(LOG, "setsockopt(TCP_KEEPIDLE) failed: %m"); + return STATUS_ERROR; + } + + port->keepalives_idle = idle; +#else + if (idle != 0) + { + elog(LOG, "setsockopt(TCP_KEEPIDLE) not supported"); + return STATUS_ERROR; + } +#endif + + return STATUS_OK; +} + +int +pq_getkeepalivesinterval(Port *port) +{ +#ifdef TCP_KEEPINTVL + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return 0; + + if (port->keepalives_interval != 0) + return port->keepalives_interval; + + if (port->default_keepalives_interval == 0) + { + socklen_t size = sizeof(port->default_keepalives_interval); + if (getsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL, + (char *) &port->default_keepalives_interval, + &size) < 0) + { + elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m"); + return -1; + } + } + + return port->default_keepalives_interval; +#else + return 0; +#endif +} + +int +pq_setkeepalivesinterval(int interval, Port *port) +{ + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return STATUS_OK; + +#ifdef TCP_KEEPINTVL + if (interval == port->keepalives_interval) + return STATUS_OK; + + if (port->default_keepalives_interval == 0) { + if (pq_getkeepalivesinterval(port) < 0) + return STATUS_ERROR; + } + + if (interval == 0) + interval = port->default_keepalives_interval; + + if (setsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL, + (char *) &interval, sizeof(interval)) < 0) + { + elog(LOG, "setsockopt(TCP_KEEPINTVL) failed: %m"); + return STATUS_ERROR; + } + + port->keepalives_interval = interval; +#else + if (interval != 0) + { + elog(LOG, "setsockopt(TCP_KEEPINTVL) not supported"); + return STATUS_ERROR; + } +#endif + + return STATUS_OK; +} + +int +pq_getkeepalivescount(Port *port) +{ +#ifdef TCP_KEEPCNT + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return 0; + + if (port->keepalives_count != 0) + return port->keepalives_count; + + if (port->default_keepalives_count == 0) + { + socklen_t size = sizeof(port->default_keepalives_count); + if (getsockopt(port->sock, SOL_TCP, TCP_KEEPCNT, + (char *) &port->default_keepalives_count, + &size) < 0) + { + elog(LOG, "getsockopt(TCP_KEEPCNT) failed: %m"); + return -1; + } + } + + return port->default_keepalives_count; +#else + return 0; +#endif +} + +int +pq_setkeepalivescount(int count, Port *port) +{ + if (IS_AF_UNIX(port->laddr.addr.ss_family)) + return STATUS_OK; + +#ifdef TCP_KEEPCNT + if (count == port->keepalives_count) + return STATUS_OK; + + if (port->default_keepalives_count == 0) { + if (pq_getkeepalivescount(port) < 0) + return STATUS_ERROR; + } + + if (count == 0) + count = port->default_keepalives_count; + + if (setsockopt(port->sock, SOL_TCP, TCP_KEEPCNT, + (char *) &count, sizeof(count)) < 0) + { + elog(LOG, "setsockopt(TCP_KEEPCNT) failed: %m"); + return STATUS_ERROR; + } + + port->keepalives_count = count; +#else + if (count != 0) + { + elog(LOG, "setsockopt(TCP_KEEPCNT) not supported"); + return STATUS_ERROR; + } +#endif + + return STATUS_OK; +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index da6aa1a9c3..f3426b18ca 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.279 2005/07/29 19:30:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.280 2005/07/30 15:17:20 momjian Exp $ * *-------------------------------------------------------------------- */ @@ -120,6 +120,12 @@ static bool assign_log_stats(bool newval, bool doit, GucSource source); static bool assign_transaction_read_only(bool newval, bool doit, GucSource source); static const char *assign_canonical_path(const char *newval, bool doit, GucSource source); +static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source); +static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source); +static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source); +static const char *show_tcp_keepalives_idle(void); +static const char *show_tcp_keepalives_interval(void); +static const char *show_tcp_keepalives_count(void); /* * GUC option variables that are exported from this module @@ -161,6 +167,9 @@ char *HbaFileName; char *IdentFileName; char *external_pid_file; +int tcp_keepalives_idle; +int tcp_keepalives_interval; +int tcp_keepalives_count; /* * These variables are all dummies that don't do anything, except in some @@ -1437,6 +1446,35 @@ static struct config_int ConfigureNamesInt[] = 500, 0, INT_MAX, NULL, NULL }, + { + {"tcp_keepalives_idle", PGC_USERSET, CLIENT_CONN_OTHER, + gettext_noop("Seconds between issuing TCP keepalives."), + gettext_noop("A value of 0 uses the system default."), + }, + &tcp_keepalives_idle, + 0, 0, INT_MAX, assign_tcp_keepalives_idle, show_tcp_keepalives_idle + }, + + { + {"tcp_keepalives_interval", PGC_USERSET, CLIENT_CONN_OTHER, + gettext_noop("Seconds between TCP keepalive retransmits."), + gettext_noop("A value of 0 uses the system default."), + }, + &tcp_keepalives_interval, + 0, 0, INT_MAX, assign_tcp_keepalives_interval, show_tcp_keepalives_interval + }, + + { + {"tcp_keepalives_count", PGC_USERSET, CLIENT_CONN_OTHER, + gettext_noop("Maximum number of TCP keepalive retransmits."), + gettext_noop("This controls the number of consecutive keepalive retransmits that can be " + "lost before a connection is considered dead. A value of 0 uses the " + "system default."), + }, + &tcp_keepalives_count, + 0, 0, INT_MAX, assign_tcp_keepalives_count, show_tcp_keepalives_count + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL @@ -5815,5 +5853,61 @@ assign_canonical_path(const char *newval, bool doit, GucSource source) return newval; } +static bool +assign_tcp_keepalives_idle(int newval, bool doit, GucSource source) +{ + if (doit && MyProcPort != NULL) + { + return (pq_setkeepalivesidle(newval, MyProcPort) == STATUS_OK); + } + + return true; +} + +static const char * +show_tcp_keepalives_idle(void) +{ + static char nbuf[32]; + snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivesidle(MyProcPort)); + return nbuf; +} + +static bool +assign_tcp_keepalives_interval(int newval, bool doit, GucSource source) +{ + if (doit && MyProcPort != NULL) + { + return (pq_setkeepalivesinterval(newval, MyProcPort) == STATUS_OK); + } + + return true; +} + +static const char * +show_tcp_keepalives_interval(void) +{ + static char nbuf[32]; + snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivesinterval(MyProcPort)); + return nbuf; +} + +static bool +assign_tcp_keepalives_count(int newval, bool doit, GucSource source) +{ + if (doit && MyProcPort != NULL) + { + return (pq_setkeepalivescount(newval, MyProcPort) == STATUS_OK); + } + + return true; +} + +static const char * +show_tcp_keepalives_count(void) +{ + static char nbuf[32]; + snprintf(nbuf, sizeof(nbuf), "%d", MyProcPort == NULL ? 0 : pq_getkeepalivescount(MyProcPort)); + return nbuf; +} #include "guc-file.c" diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index db8c28814d..5eabe09552 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -70,6 +70,11 @@ #krb_caseins_users = off #krb_srvname = 'postgres' +# - TCP Keepalives - +# see 'man 7 tcp' for details +#tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; 0 uses the system default. +#tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; 0 uses the system default. +#tcp_keepalives_count = 0 # TCP_KEEPCNT, in seconds; 0 uses the system default. #--------------------------------------------------------------------------- # RESOURCE USAGE (except WAL) diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 80441733fb..2be131993b 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2005, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.135 2005/07/28 22:14:30 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.136 2005/07/30 15:17:22 momjian Exp $ */ /*---------------------------------------------------------------------- @@ -601,6 +601,9 @@ psql_completion(char *text, int start, int end) "superuser_reserved_connections", "syslog_facility", "syslog_ident", + "tcp_keepalives_idle", + "tcp_keepalives_interval", + "tcp_keepalives_count", "temp_buffers", "TimeZone", "trace_notify", diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index cec679577b..bbc218a0e1 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.49 2004/12/31 22:03:32 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/libpq/libpq-be.h,v 1.50 2005/07/30 15:17:25 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,9 @@ #include #include #endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif #include "libpq/hba.h" #include "libpq/pqcomm.h" @@ -92,9 +95,37 @@ typedef struct Port char peer_cn[SM_USER + 1]; unsigned long count; #endif + + /* + * TCP keepalive settings; + * default values are 0 if AF_UNIX or not yet known; + * current values are 0 if AF_UNIX or using the default. + */ +#ifdef TCP_KEEPIDLE + int default_keepalives_idle; + int keepalives_idle; +#endif +#ifdef TCP_KEEPINTVL + int default_keepalives_interval; + int keepalives_interval; +#endif +#ifdef TCP_KEEPCNT + int default_keepalives_count; + int keepalives_count; +#endif } Port; extern ProtocolVersion FrontendProtocol; +/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ + +extern int pq_getkeepalivesidle(Port *port); +extern int pq_getkeepalivesinterval(Port *port); +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); + #endif /* LIBPQ_BE_H */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index c08ecd4db4..00399bd488 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2005, PostgreSQL Global Development Group * Written by Peter Eisentraut . * - * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.61 2005/06/26 03:04:12 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.62 2005/07/30 15:17:26 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H @@ -134,6 +134,9 @@ extern char *HbaFileName; extern char *IdentFileName; extern char *external_pid_file; +extern int tcp_keepalives_idle; +extern int tcp_keepalives_interval; +extern int tcp_keepalives_count; extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); -- 2.40.0