Skip site navigation (1) Skip section navigation (2)

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>
To: Oliver Jowett <oliver(at)opencloud(dot)com>
Cc: pgsql-patches(at)postgresql(dot)org, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Subject: Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL
Date: 2005-07-29 14:58:31
Message-ID: 200507291458.j6TEwVW00616@candle.pha.pa.us (view raw or flat)
Thread:
Lists: pgsql-hackerspgsql-patches
Is this the functionality we agreed we wanted?

---------------------------------------------------------------------------

Oliver Jowett wrote:
> Bruce Momjian wrote:
> > Is this patch being worked on?
> 
> Here's an updated version. It compiles and appears to work as expected
> under Linux (supports TCP_KEEPIDLE etc) and Solaris 9 (no support).
> 
> Main changes:
> 
> - removed the tcp_keepalives GUC, SO_KEEPALIVE is now always on (as in
> current CVS)
> - {get,set}sockopt calls are only done when absolutely necessary (no
> extra syscalls during backend startup in a default configuration).
> 
> I still haven't had a chance to glue in support for the TCP_KEEPALIVE
> (Solaris-style) option, but that should be fairly painless to add later.
> 
> -O

> ? postgresql-8.1devel.tar.gz
> Index: doc/src/sgml/runtime.sgml
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/doc/src/sgml/runtime.sgml,v
> retrieving revision 1.335
> diff -u -c -r1.335 runtime.sgml
> *** doc/src/sgml/runtime.sgml	2 Jul 2005 19:16:36 -0000	1.335
> --- doc/src/sgml/runtime.sgml	4 Jul 2005 10:41:33 -0000
> ***************
> *** 894,899 ****
> --- 894,946 ----
>         </listitem>
>        </varlistentry>
>        
> +      <varlistentry id="guc-tcp-keepalives-idle" xreflabel="tcp_keepalives_idle">
> +       <term><varname>tcp_keepalives_idle</varname> (<type>integer</type>)</term>
> +       <indexterm>
> +        <primary><varname>tcp_keepalives_idle</> configuration parameter</primary>
> +       </indexterm>
> +       <listitem>
> +        <para>
> +         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.
> +        </para>
> +       </listitem>
> +      </varlistentry>
> +      
> +      <varlistentry id="guc-tcp-keepalives-interval" xreflabel="tcp_keepalives_interval">
> +       <term><varname>tcp_keepalives_interval</varname> (<type>integer</type>)</term>
> +       <indexterm>
> +        <primary><varname>tcp_keepalives_interval</> configuration parameter</primary>
> +       </indexterm>
> +       <listitem>
> +        <para>
> +         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.
> +        </para>
> +       </listitem>
> +      </varlistentry>
> +      
> +      <varlistentry id="guc-tcp-keepalives-count" xreflabel="tcp_keepalives_count">
> +       <term><varname>tcp_keepalives_count</varname> (<type>integer</type>)</term>
> +       <indexterm>
> +        <primary><varname>tcp_keepalives_count</> configuration parameter</primary>
> +       </indexterm>
> +       <listitem>
> +        <para>
> +         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.
> +        </para>
> +       </listitem>
> +      </varlistentry>
> +      
>        </variablelist>
>        </sect3>
>        <sect3 id="runtime-config-connection-security">
> Index: src/backend/libpq/pqcomm.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/libpq/pqcomm.c,v
> retrieving revision 1.176
> diff -u -c -r1.176 pqcomm.c
> *** src/backend/libpq/pqcomm.c	22 Feb 2005 04:35:57 -0000	1.176
> --- src/backend/libpq/pqcomm.c	4 Jul 2005 10:41:33 -0000
> ***************
> *** 87,93 ****
>   #include "libpq/libpq.h"
>   #include "miscadmin.h"
>   #include "storage/ipc.h"
> ! 
>   
>   /*
>    * Configuration options
> --- 87,93 ----
>   #include "libpq/libpq.h"
>   #include "miscadmin.h"
>   #include "storage/ipc.h"
> ! #include "utils/guc.h"
>   
>   /*
>    * Configuration options
> ***************
> *** 594,599 ****
> --- 594,612 ----
>   			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,1160 ****
> --- 1171,1369 ----
>   	/* 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;
> + }
> Index: src/backend/utils/misc/guc.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v
> retrieving revision 1.271
> diff -u -c -r1.271 guc.c
> *** src/backend/utils/misc/guc.c	28 Jun 2005 05:09:02 -0000	1.271
> --- src/backend/utils/misc/guc.c	4 Jul 2005 10:41:33 -0000
> ***************
> *** 117,122 ****
> --- 117,128 ----
>   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
> ***************
> *** 158,163 ****
> --- 164,172 ----
>   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
> ***************
> *** 1375,1380 ****
> --- 1384,1418 ----
>   		BLCKSZ, BLCKSZ, BLCKSZ, 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
> ***************
> *** 5744,5748 ****
> --- 5782,5842 ----
>   		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"
> Index: src/backend/utils/misc/postgresql.conf.sample
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v
> retrieving revision 1.151
> diff -u -c -r1.151 postgresql.conf.sample
> *** src/backend/utils/misc/postgresql.conf.sample	2 Jul 2005 18:46:45 -0000	1.151
> --- src/backend/utils/misc/postgresql.conf.sample	4 Jul 2005 10:41:33 -0000
> ***************
> *** 70,75 ****
> --- 70,80 ----
>   #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)
> Index: src/bin/psql/tab-complete.c
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/bin/psql/tab-complete.c,v
> retrieving revision 1.133
> diff -u -c -r1.133 tab-complete.c
> *** src/bin/psql/tab-complete.c	22 Jun 2005 21:14:30 -0000	1.133
> --- src/bin/psql/tab-complete.c	4 Jul 2005 10:41:33 -0000
> ***************
> *** 600,605 ****
> --- 600,608 ----
>   		"superuser_reserved_connections",
>   		"syslog_facility",
>   		"syslog_ident",
> + 		"tcp_keepalives_idle",
> + 		"tcp_keepalives_interval",
> + 		"tcp_keepalives_count",
>   		"temp_buffers",
>   		"TimeZone",
>   		"trace_notify",
> Index: src/include/libpq/libpq-be.h
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/include/libpq/libpq-be.h,v
> retrieving revision 1.49
> diff -u -c -r1.49 libpq-be.h
> *** src/include/libpq/libpq-be.h	31 Dec 2004 22:03:32 -0000	1.49
> --- src/include/libpq/libpq-be.h	4 Jul 2005 10:41:33 -0000
> ***************
> *** 25,30 ****
> --- 25,33 ----
>   #include <openssl/ssl.h>
>   #include <openssl/err.h>
>   #endif
> + #ifdef HAVE_NETINET_TCP_H
> + #include <netinet/tcp.h>
> + #endif
>   
>   #include "libpq/hba.h"
>   #include "libpq/pqcomm.h"
> ***************
> *** 92,100 ****
> --- 95,131 ----
>   	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 */
> Index: src/include/utils/guc.h
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/src/include/utils/guc.h,v
> retrieving revision 1.61
> diff -u -c -r1.61 guc.h
> *** src/include/utils/guc.h	26 Jun 2005 03:04:12 -0000	1.61
> --- src/include/utils/guc.h	4 Jul 2005 10:41:33 -0000
> ***************
> *** 134,139 ****
> --- 134,142 ----
>   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);

> 
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Don't 'kill -9' the postmaster

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman(at)candle(dot)pha(dot)pa(dot)us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

In response to

Responses

pgsql-hackers by date

Next:From: Tom LaneDate: 2005-07-29 15:19:34
Subject: Re: [HACKERS] Autovacuum loose ends
Previous:From: Bruce MomjianDate: 2005-07-29 14:47:02
Subject: Re: [HACKERS] Dbsize backend integration

pgsql-patches by date

Next:From: Bruce MomjianDate: 2005-07-29 15:13:38
Subject: Re: reindexdb into scripts
Previous:From: Bruce MomjianDate: 2005-07-29 14:47:02
Subject: Re: [HACKERS] Dbsize backend integration

Privacy Policy | About PostgreSQL
Copyright © 1996-2014 The PostgreSQL Global Development Group