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

Reply via email to