Thread: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL (was Re: [HACKERS] Feature freeze date for 8.1)

Tom Lane wrote:
> Oliver Jowett <oliver@opencloud.com> writes:
>
>>Tom Lane wrote:
>>
>>>I'm not convinced that Postgres ought to provide
>>>a way to second-guess the TCP stack ...
>
>
>>Would you be ok with a patch that allowed configuration of the
>>TCP_KEEPCNT / TCP_KEEPIDLE / TCP_KEEPINTVL socket options on backend
>>sockets?
>
>
> [ shrug... ]  As long as it doesn't fail to build on platforms that
> don't offer those options, I couldn't complain too hard.  But do we
> really need all that?

Here's a patch that adds four new GUCs:

  tcp_keepalives (defaults to on, controls SO_KEEPALIVE)
  tcp_keepalives_idle (controls TCP_KEEPIDLE)
  tcp_keepalives_interval (controls TCP_KEEPINTVL)
  tcp_keepalives_count (controls TCP_KEEPCNT)

They're ignored for AF_UNIX connections and when running standalone.

tcp_keepalives_* treat 0 as "use system default". If the underlying OS
doesn't provide the TCP_KEEP* socket options, the GUCs are present but
reject any value other than 0.

SHOW reflects the currently-in-use value or 0 if not applicable or not
known. i.e. if you set it to 0 and you have the socket options
available, SHOW will show the result of getsockopt() which is non-zero.

A default install on my Linux system produces:

template1=# show all;
              name              |                   setting

--------------------------------+----------------------------------------------
[...]
 tcp_keepalives                 | on
 tcp_keepalives_count           | 9
 tcp_keepalives_idle            | 7200
 tcp_keepalives_interval        | 75
[...]

I haven't had a chance to check it builds on other systems or to test
that this handles actual network failures nicely yet.

-O
Index: doc/src/sgml/runtime.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/runtime.sgml,v
retrieving revision 1.315
diff -c -r1.315 runtime.sgml
*** doc/src/sgml/runtime.sgml    23 Apr 2005 03:27:40 -0000    1.315
--- doc/src/sgml/runtime.sgml    3 May 2005 01:44:02 -0000
***************
*** 889,894 ****
--- 889,961 ----
        </listitem>
       </varlistentry>

+      <varlistentry id="guc-tcp-keepalives" xreflabel="tcp_keepalives">
+       <term><varname>tcp_keepalives</varname> (<type>boolean</type>)</term>
+       <indexterm>
+        <primary><varname>tcp_keepalives</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Controls the use of TCP keepalives on client connections. When enabled,
+         idle connections will be periodically probed to check that the client
+         is still present. If sufficient probes are lost, the connection will
+         be broken. This option is ignored for connections made via a
+         Unix-domain socket.
+        </para>
+       </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, and will have no effect
+         unless the <varname>tcp_keepalives</varname> option is enabled.
+        </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, and will have no effect
+         unless the <varname>tcp_keepalives</varname> option is enabled.
+        </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. This option is ignored for
+         connections made via a Unix-domain socket, and will have no effect
+         unless the <varname>tcp_keepalives</varname> option is enabled.
+        </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 -c -r1.176 pqcomm.c
*** src/backend/libpq/pqcomm.c    22 Feb 2005 04:35:57 -0000    1.176
--- src/backend/libpq/pqcomm.c    3 May 2005 01:44:03 -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
***************
*** 577,582 ****
--- 577,583 ----
      if (!IS_AF_UNIX(port->laddr.addr.ss_family))
      {
          int            on;
+         socklen_t   size;

  #ifdef    TCP_NODELAY
          on = 1;
***************
*** 587,599 ****
              return STATUS_ERROR;
          }
  #endif
!         on = 1;
!         if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
!                        (char *) &on, sizeof(on)) < 0)
          {
!             elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
              return STATUS_ERROR;
          }
      }

      return STATUS_OK;
--- 588,645 ----
              return STATUS_ERROR;
          }
  #endif
!
!         if (pq_setkeepalives(tcp_keepalives, port) != STATUS_OK)
!             return STATUS_ERROR;
!
!         /* Grab default keepalive values, then apply
!          * our GUC settings.
!          */
! #ifdef TCP_KEEPIDLE
!         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE,
!                        (char *) &port->default_keepalives_idle, &size) < 0)
!         {
!             elog(LOG, "getsockopt(TCP_KEEPIDLE) failed: %m");
!             return STATUS_ERROR;
!         }
! #else
!         port->default_keepalives_idle = 0;
! #endif
!
! #ifdef TCP_KEEPINTVL
!         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL,
!                        (char *) &port->default_keepalives_interval, &size) < 0)
          {
!             elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
              return STATUS_ERROR;
          }
+ #else
+         port->default_keepalives_idle = 0;
+ #endif
+
+ #ifdef TCP_KEEPCNT
+         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPCNT,
+                        (char *) &port->default_keepalives_count, &size) < 0)
+         {
+             elog(LOG, "getsockopt(TCP_KEEPCNT) failed: %m");
+             return STATUS_ERROR;
+         }
+ #else
+         port->default_keepalives_idle = 0;
+ #endif
+
+         /* 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 ****
--- 1204,1305 ----
      /* in non-error case, copy.c will have emitted the terminator line */
      DoingCopyOut = false;
  }
+
+ int
+ pq_setkeepalives(bool onoff, Port *port)
+ {
+     int on = (onoff ? 1 : 0);
+
+     if (IS_AF_UNIX(port->laddr.addr.ss_family))
+         return STATUS_OK;
+
+     if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
+                    (char *) &on, sizeof(on)) < 0)
+     {
+         elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
+         return STATUS_ERROR;
+     }
+
+     return STATUS_OK;
+ }
+
+ int
+ pq_setkeepalivesidle(int idle, Port *port)
+ {
+     if (IS_AF_UNIX(port->laddr.addr.ss_family))
+         return STATUS_OK;
+
+ #ifdef TCP_KEEPIDLE
+     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;
+     }
+ #else
+     if (idle != 0)
+     {
+         elog(LOG, "setsockopt(TCP_KEEPIDLE) not supported");
+         return STATUS_ERROR;
+     }
+ #endif
+
+     return STATUS_OK;
+ }
+
+ int
+ pq_setkeepalivesinterval(int interval, Port *port)
+ {
+     if (IS_AF_UNIX(port->laddr.addr.ss_family))
+         return STATUS_OK;
+
+ #ifdef TCP_KEEPINTVL
+     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;
+     }
+ #else
+     if (interval != 0)
+     {
+         elog(LOG, "setsockopt(TCP_KEEPINTVL) not supported");
+         return STATUS_ERROR;
+     }
+ #endif
+
+     return STATUS_OK;
+ }
+
+ int
+ pq_setkeepalivescount(int count, Port *port)
+ {
+     if (IS_AF_UNIX(port->laddr.addr.ss_family))
+         return STATUS_OK;
+
+ #ifdef TCP_KEEPCNT
+     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;
+     }
+ #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.261
diff -c -r1.261 guc.c
*** src/backend/utils/misc/guc.c    1 May 2005 18:56:19 -0000    1.261
--- src/backend/utils/misc/guc.c    3 May 2005 01:44:08 -0000
***************
*** 113,118 ****
--- 113,125 ----
  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(bool 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
***************
*** 154,159 ****
--- 161,170 ----
  char       *IdentFileName;
  char       *external_pid_file;

+ bool        tcp_keepalives = true;
+ 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
***************
*** 860,865 ****
--- 871,885 ----
  #endif
      },

+     {
+         {"tcp_keepalives", PGC_USERSET, CLIENT_CONN_OTHER,
+              gettext_noop("Use keepalives on client TCP connections."),
+              NULL,
+         },
+         &tcp_keepalives,
+         true, assign_tcp_keepalives, NULL
+     },
+
      /* End-of-list marker */
      {
          {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
***************
*** 1333,1338 ****
--- 1353,1387 ----
          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
***************
*** 5677,5681 ****
--- 5726,5802 ----
          return newval;
  }

+ static bool
+ assign_tcp_keepalives(bool newval, bool doit, GucSource source)
+ {
+     if (doit && MyProcPort != NULL)
+     {
+         pq_setkeepalives(newval, MyProcPort);
+     }
+     return true;
+ }
+
+ 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",
+              (tcp_keepalives_idle != 0 || MyProcPort == NULL)
+              ? tcp_keepalives_idle : MyProcPort->default_keepalives_idle);
+     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",
+              (tcp_keepalives_interval != 0 || MyProcPort == NULL)
+              ? tcp_keepalives_interval : MyProcPort->default_keepalives_interval);
+     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",
+              (tcp_keepalives_count != 0 || MyProcPort == NULL)
+              ? tcp_keepalives_count : MyProcPort->default_keepalives_count);
+     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.140
diff -c -r1.140 postgresql.conf.sample
*** src/backend/utils/misc/postgresql.conf.sample    21 Apr 2005 19:18:13 -0000    1.140
--- src/backend/utils/misc/postgresql.conf.sample    3 May 2005 01:44:08 -0000
***************
*** 66,71 ****
--- 66,77 ----
  #krb_server_keyfile = ''
  #db_user_namespace = false

+ # - TCP Keepalives -
+ # see 'man 7 tcp' for details
+ #tcp_keepalives = on            # enables use of TCP keepalives
+ #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.125
diff -c -r1.125 tab-complete.c
*** src/bin/psql/tab-complete.c    21 Apr 2005 19:18:13 -0000    1.125
--- src/bin/psql/tab-complete.c    3 May 2005 01:44:10 -0000
***************
*** 595,600 ****
--- 595,604 ----
          "superuser_reserved_connections",
          "syslog_facility",
          "syslog_ident",
+         "tcp_keepalives",
+         "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 -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    3 May 2005 01:44:10 -0000
***************
*** 92,100 ****
--- 92,113 ----
      char        peer_cn[SM_USER + 1];
      unsigned long count;
  #endif
+
+     /*
+      * Default TCP keepalive values found after accept(); 0 if unsupported or AF_UNIX.
+      */
+     int         default_keepalives_idle;
+     int         default_keepalives_interval;
+     int         default_keepalives_count;
  } Port;


  extern ProtocolVersion FrontendProtocol;

+ /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
+ extern int pq_setkeepalives(bool onoff, 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.60
diff -c -r1.60 guc.h
*** src/include/utils/guc.h    25 Mar 2005 16:17:28 -0000    1.60
--- src/include/utils/guc.h    3 May 2005 01:44:10 -0000
***************
*** 133,138 ****
--- 133,142 ----
  extern char *IdentFileName;
  extern char *external_pid_file;

+ extern bool tcp_keepalives;
+ 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);

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
Oliver Jowett wrote:

> Here's a patch that adds four new GUCs:

> I haven't had a chance to check it builds on other systems or to test
> that this handles actual network failures nicely yet.

The patch builds OK on Solaris 9, which doesn't have the required socket
options.

Solaris (and some other OSes, apparently) supports TCP_KEEPALIVE instead
of TCP_KEEPIDLE/TCP_KEEPCNT/TCP_KEEPINTVL. I will look at adding support
for that next.

-O

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Bruce Momjian
Date:
Where are we on this?  I do think it solves a problem that some are
having, and it seems it would detect a disconnected client and abort a
long running query.

I am not very excited about adding four more GUC variables, and I am
thinking we could just have it use the OS defaults and see if we need
more later, so that would add only one GUC.

It compiles/tests fine on my BSD system.

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

Oliver Jowett wrote:
> Tom Lane wrote:
> > Oliver Jowett <oliver@opencloud.com> writes:
> >
> >>Tom Lane wrote:
> >>
> >>>I'm not convinced that Postgres ought to provide
> >>>a way to second-guess the TCP stack ...
> >
> >
> >>Would you be ok with a patch that allowed configuration of the
> >>TCP_KEEPCNT / TCP_KEEPIDLE / TCP_KEEPINTVL socket options on backend
> >>sockets?
> >
> >
> > [ shrug... ]  As long as it doesn't fail to build on platforms that
> > don't offer those options, I couldn't complain too hard.  But do we
> > really need all that?
>
> Here's a patch that adds four new GUCs:
>
>   tcp_keepalives (defaults to on, controls SO_KEEPALIVE)
>   tcp_keepalives_idle (controls TCP_KEEPIDLE)
>   tcp_keepalives_interval (controls TCP_KEEPINTVL)
>   tcp_keepalives_count (controls TCP_KEEPCNT)
>
> They're ignored for AF_UNIX connections and when running standalone.
>
> tcp_keepalives_* treat 0 as "use system default". If the underlying OS
> doesn't provide the TCP_KEEP* socket options, the GUCs are present but
> reject any value other than 0.
>
> SHOW reflects the currently-in-use value or 0 if not applicable or not
> known. i.e. if you set it to 0 and you have the socket options
> available, SHOW will show the result of getsockopt() which is non-zero.
>
> A default install on my Linux system produces:
>
> template1=# show all;
>               name              |                   setting
>
> --------------------------------+----------------------------------------------
> [...]
>  tcp_keepalives                 | on
>  tcp_keepalives_count           | 9
>  tcp_keepalives_idle            | 7200
>  tcp_keepalives_interval        | 75
> [...]
>
> I haven't had a chance to check it builds on other systems or to test
> that this handles actual network failures nicely yet.
>
> -O

> Index: doc/src/sgml/runtime.sgml
> ===================================================================
> RCS file: /projects/cvsroot/pgsql/doc/src/sgml/runtime.sgml,v
> retrieving revision 1.315
> diff -c -r1.315 runtime.sgml
> *** doc/src/sgml/runtime.sgml    23 Apr 2005 03:27:40 -0000    1.315
> --- doc/src/sgml/runtime.sgml    3 May 2005 01:44:02 -0000
> ***************
> *** 889,894 ****
> --- 889,961 ----
>         </listitem>
>        </varlistentry>
>
> +      <varlistentry id="guc-tcp-keepalives" xreflabel="tcp_keepalives">
> +       <term><varname>tcp_keepalives</varname> (<type>boolean</type>)</term>
> +       <indexterm>
> +        <primary><varname>tcp_keepalives</> configuration parameter</primary>
> +       </indexterm>
> +       <listitem>
> +        <para>
> +         Controls the use of TCP keepalives on client connections. When enabled,
> +         idle connections will be periodically probed to check that the client
> +         is still present. If sufficient probes are lost, the connection will
> +         be broken. This option is ignored for connections made via a
> +         Unix-domain socket.
> +        </para>
> +       </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, and will have no effect
> +         unless the <varname>tcp_keepalives</varname> option is enabled.
> +        </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, and will have no effect
> +         unless the <varname>tcp_keepalives</varname> option is enabled.
> +        </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. This option is ignored for
> +         connections made via a Unix-domain socket, and will have no effect
> +         unless the <varname>tcp_keepalives</varname> option is enabled.
> +        </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 -c -r1.176 pqcomm.c
> *** src/backend/libpq/pqcomm.c    22 Feb 2005 04:35:57 -0000    1.176
> --- src/backend/libpq/pqcomm.c    3 May 2005 01:44:03 -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
> ***************
> *** 577,582 ****
> --- 577,583 ----
>       if (!IS_AF_UNIX(port->laddr.addr.ss_family))
>       {
>           int            on;
> +         socklen_t   size;
>
>   #ifdef    TCP_NODELAY
>           on = 1;
> ***************
> *** 587,599 ****
>               return STATUS_ERROR;
>           }
>   #endif
> !         on = 1;
> !         if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
> !                        (char *) &on, sizeof(on)) < 0)
>           {
> !             elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
>               return STATUS_ERROR;
>           }
>       }
>
>       return STATUS_OK;
> --- 588,645 ----
>               return STATUS_ERROR;
>           }
>   #endif
> !
> !         if (pq_setkeepalives(tcp_keepalives, port) != STATUS_OK)
> !             return STATUS_ERROR;
> !
> !         /* Grab default keepalive values, then apply
> !          * our GUC settings.
> !          */
> ! #ifdef TCP_KEEPIDLE
> !         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPIDLE,
> !                        (char *) &port->default_keepalives_idle, &size) < 0)
> !         {
> !             elog(LOG, "getsockopt(TCP_KEEPIDLE) failed: %m");
> !             return STATUS_ERROR;
> !         }
> ! #else
> !         port->default_keepalives_idle = 0;
> ! #endif
> !
> ! #ifdef TCP_KEEPINTVL
> !         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPINTVL,
> !                        (char *) &port->default_keepalives_interval, &size) < 0)
>           {
> !             elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
>               return STATUS_ERROR;
>           }
> + #else
> +         port->default_keepalives_idle = 0;
> + #endif
> +
> + #ifdef TCP_KEEPCNT
> +         if (getsockopt(port->sock, SOL_TCP, TCP_KEEPCNT,
> +                        (char *) &port->default_keepalives_count, &size) < 0)
> +         {
> +             elog(LOG, "getsockopt(TCP_KEEPCNT) failed: %m");
> +             return STATUS_ERROR;
> +         }
> + #else
> +         port->default_keepalives_idle = 0;
> + #endif
> +
> +         /* 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 ****
> --- 1204,1305 ----
>       /* in non-error case, copy.c will have emitted the terminator line */
>       DoingCopyOut = false;
>   }
> +
> + int
> + pq_setkeepalives(bool onoff, Port *port)
> + {
> +     int on = (onoff ? 1 : 0);
> +
> +     if (IS_AF_UNIX(port->laddr.addr.ss_family))
> +         return STATUS_OK;
> +
> +     if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
> +                    (char *) &on, sizeof(on)) < 0)
> +     {
> +         elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
> +         return STATUS_ERROR;
> +     }
> +
> +     return STATUS_OK;
> + }
> +
> + int
> + pq_setkeepalivesidle(int idle, Port *port)
> + {
> +     if (IS_AF_UNIX(port->laddr.addr.ss_family))
> +         return STATUS_OK;
> +
> + #ifdef TCP_KEEPIDLE
> +     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;
> +     }
> + #else
> +     if (idle != 0)
> +     {
> +         elog(LOG, "setsockopt(TCP_KEEPIDLE) not supported");
> +         return STATUS_ERROR;
> +     }
> + #endif
> +
> +     return STATUS_OK;
> + }
> +
> + int
> + pq_setkeepalivesinterval(int interval, Port *port)
> + {
> +     if (IS_AF_UNIX(port->laddr.addr.ss_family))
> +         return STATUS_OK;
> +
> + #ifdef TCP_KEEPINTVL
> +     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;
> +     }
> + #else
> +     if (interval != 0)
> +     {
> +         elog(LOG, "setsockopt(TCP_KEEPINTVL) not supported");
> +         return STATUS_ERROR;
> +     }
> + #endif
> +
> +     return STATUS_OK;
> + }
> +
> + int
> + pq_setkeepalivescount(int count, Port *port)
> + {
> +     if (IS_AF_UNIX(port->laddr.addr.ss_family))
> +         return STATUS_OK;
> +
> + #ifdef TCP_KEEPCNT
> +     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;
> +     }
> + #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.261
> diff -c -r1.261 guc.c
> *** src/backend/utils/misc/guc.c    1 May 2005 18:56:19 -0000    1.261
> --- src/backend/utils/misc/guc.c    3 May 2005 01:44:08 -0000
> ***************
> *** 113,118 ****
> --- 113,125 ----
>   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(bool 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
> ***************
> *** 154,159 ****
> --- 161,170 ----
>   char       *IdentFileName;
>   char       *external_pid_file;
>
> + bool        tcp_keepalives = true;
> + 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
> ***************
> *** 860,865 ****
> --- 871,885 ----
>   #endif
>       },
>
> +     {
> +         {"tcp_keepalives", PGC_USERSET, CLIENT_CONN_OTHER,
> +              gettext_noop("Use keepalives on client TCP connections."),
> +              NULL,
> +         },
> +         &tcp_keepalives,
> +         true, assign_tcp_keepalives, NULL
> +     },
> +
>       /* End-of-list marker */
>       {
>           {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
> ***************
> *** 1333,1338 ****
> --- 1353,1387 ----
>           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
> ***************
> *** 5677,5681 ****
> --- 5726,5802 ----
>           return newval;
>   }
>
> + static bool
> + assign_tcp_keepalives(bool newval, bool doit, GucSource source)
> + {
> +     if (doit && MyProcPort != NULL)
> +     {
> +         pq_setkeepalives(newval, MyProcPort);
> +     }
> +     return true;
> + }
> +
> + 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",
> +              (tcp_keepalives_idle != 0 || MyProcPort == NULL)
> +              ? tcp_keepalives_idle : MyProcPort->default_keepalives_idle);
> +     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",
> +              (tcp_keepalives_interval != 0 || MyProcPort == NULL)
> +              ? tcp_keepalives_interval : MyProcPort->default_keepalives_interval);
> +     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",
> +              (tcp_keepalives_count != 0 || MyProcPort == NULL)
> +              ? tcp_keepalives_count : MyProcPort->default_keepalives_count);
> +     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.140
> diff -c -r1.140 postgresql.conf.sample
> *** src/backend/utils/misc/postgresql.conf.sample    21 Apr 2005 19:18:13 -0000    1.140
> --- src/backend/utils/misc/postgresql.conf.sample    3 May 2005 01:44:08 -0000
> ***************
> *** 66,71 ****
> --- 66,77 ----
>   #krb_server_keyfile = ''
>   #db_user_namespace = false
>
> + # - TCP Keepalives -
> + # see 'man 7 tcp' for details
> + #tcp_keepalives = on            # enables use of TCP keepalives
> + #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.125
> diff -c -r1.125 tab-complete.c
> *** src/bin/psql/tab-complete.c    21 Apr 2005 19:18:13 -0000    1.125
> --- src/bin/psql/tab-complete.c    3 May 2005 01:44:10 -0000
> ***************
> *** 595,600 ****
> --- 595,604 ----
>           "superuser_reserved_connections",
>           "syslog_facility",
>           "syslog_ident",
> +         "tcp_keepalives",
> +         "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 -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    3 May 2005 01:44:10 -0000
> ***************
> *** 92,100 ****
> --- 92,113 ----
>       char        peer_cn[SM_USER + 1];
>       unsigned long count;
>   #endif
> +
> +     /*
> +      * Default TCP keepalive values found after accept(); 0 if unsupported or AF_UNIX.
> +      */
> +     int         default_keepalives_idle;
> +     int         default_keepalives_interval;
> +     int         default_keepalives_count;
>   } Port;
>
>
>   extern ProtocolVersion FrontendProtocol;
>
> + /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
> + extern int pq_setkeepalives(bool onoff, 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.60
> diff -c -r1.60 guc.h
> *** src/include/utils/guc.h    25 Mar 2005 16:17:28 -0000    1.60
> --- src/include/utils/guc.h    3 May 2005 01:44:10 -0000
> ***************
> *** 133,138 ****
> --- 133,142 ----
>   extern char *IdentFileName;
>   extern char *external_pid_file;
>
> + extern bool tcp_keepalives;
> + 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 5: Have you checked our extensive FAQ?
>
>                http://www.postgresql.org/docs/faq

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

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
Bruce Momjian wrote:
> Where are we on this?  I do think it solves a problem that some are
> having, and it seems it would detect a disconnected client and abort a
> long running query.

I haven't had a chance to do any work to support the Solaris stuff, but
otherwise I'm just waiting on review.

Note that the patch doesn't do anything about long-running queries, it's
really about long-running connections/transactions. The server will only
notice the disconnected client once it tries to read/write something.

> I am not very excited about adding four more GUC variables, and I am
> thinking we could just have it use the OS defaults and see if we need
> more later, so that would add only one GUC.

The whole point of the patch is that the system-wide defaults are
usually not useful. The current, unpatched, server turns on SO_KEEPALIVE
always, but the typical OS defaults set the timeout before probing to
about 2 hours.

-O

Oliver Jowett <oliver@opencloud.com> writes:
> Here's a patch that adds four new GUCs:

>   tcp_keepalives (defaults to on, controls SO_KEEPALIVE)
>   tcp_keepalives_idle (controls TCP_KEEPIDLE)
>   tcp_keepalives_interval (controls TCP_KEEPINTVL)
>   tcp_keepalives_count (controls TCP_KEEPCNT)

Do you think the system defaults are really going to be port-specific?
I'm slightly annoyed by the number of syscalls this adds to every
connection startup ... getting rid of redundant getsockopt calls would
help.  Getting rid of redundant setsockopt calls (ie, a call to
establish the value that we already know is in force) would help more.

Alternatively, we could lose the frammish that PostgreSQL can tell you
what the system defaults are: 0 in the GUC just means "do nothing",
not "find out what the current setting is on the off chance that the
user might want to know".

Or, if you really think that's important, it could be left to the SHOW
routines to extract the value on-demand.  That is:
    GUC = 0: do nothing at connection start
    GUC != 0: setsockopt at connection start
    SHOW: do getsockopt and report result

            regards, tom lane

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> I am not very excited about adding four more GUC variables, and I am
> thinking we could just have it use the OS defaults and see if we need
> more later, so that would add only one GUC.

Huh?  "Use the OS defaults" is where we are now.

I'm unconvinced of the value of allowing people to turn off keepalive
entirely, and therefore I question the need for the boolean
tcp_keepalives GUC, but the other three appear useful.

            regards, tom lane

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
Tom Lane wrote:
> Oliver Jowett <oliver@opencloud.com> writes:
>
>>Here's a patch that adds four new GUCs:
>
>
>>  tcp_keepalives (defaults to on, controls SO_KEEPALIVE)
>>  tcp_keepalives_idle (controls TCP_KEEPIDLE)
>>  tcp_keepalives_interval (controls TCP_KEEPINTVL)
>>  tcp_keepalives_count (controls TCP_KEEPCNT)
>
> Do you think the system defaults are really going to be port-specific?

I don't understand what you mean. TCP_* override the system defaults on
a per-connection basis, if that's what you mean.

> I'm slightly annoyed by the number of syscalls this adds to every
> connection startup ... getting rid of redundant getsockopt calls would
> help.  Getting rid of redundant setsockopt calls (ie, a call to
> establish the value that we already know is in force) would help more.

I originally did this but went in favor of simpler code. Are the extra
syscalls an issue? I didn't think they were that expensive..

> Alternatively, we could lose the frammish that PostgreSQL can tell you
> what the system defaults are: 0 in the GUC just means "do nothing",
> not "find out what the current setting is on the off chance that the
> user might want to know".

I didn't do this as it meant that using SET tcp_whatever = 0 does *not*
reset the setting to what you'd get with a value of 0 in
postgresql.conf, which seemed confusing to me.

This could all get simpler if we didn't allow per-connection changing of
that GUC (i.e. set it once at startup and forget about it), which
probably isn't unreasonable. I wonder if those socket options get
inherited from the listening socket? Will check.

-O

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Bruce Momjian
Date:
Is this patch being worked on?

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

Oliver Jowett wrote:
> Tom Lane wrote:
> > Oliver Jowett <oliver@opencloud.com> writes:
> >
> >>Here's a patch that adds four new GUCs:
> >
> >
> >>  tcp_keepalives (defaults to on, controls SO_KEEPALIVE)
> >>  tcp_keepalives_idle (controls TCP_KEEPIDLE)
> >>  tcp_keepalives_interval (controls TCP_KEEPINTVL)
> >>  tcp_keepalives_count (controls TCP_KEEPCNT)
> >
> > Do you think the system defaults are really going to be port-specific?
>
> I don't understand what you mean. TCP_* override the system defaults on
> a per-connection basis, if that's what you mean.
>
> > I'm slightly annoyed by the number of syscalls this adds to every
> > connection startup ... getting rid of redundant getsockopt calls would
> > help.  Getting rid of redundant setsockopt calls (ie, a call to
> > establish the value that we already know is in force) would help more.
>
> I originally did this but went in favor of simpler code. Are the extra
> syscalls an issue? I didn't think they were that expensive..
>
> > Alternatively, we could lose the frammish that PostgreSQL can tell you
> > what the system defaults are: 0 in the GUC just means "do nothing",
> > not "find out what the current setting is on the off chance that the
> > user might want to know".
>
> I didn't do this as it meant that using SET tcp_whatever = 0 does *not*
> reset the setting to what you'd get with a value of 0 in
> postgresql.conf, which seemed confusing to me.
>
> This could all get simpler if we didn't allow per-connection changing of
> that GUC (i.e. set it once at startup and forget about it), which
> probably isn't unreasonable. I wonder if those socket options get
> inherited from the listening socket? Will check.
>
> -O
>
> ---------------------------(end of broadcast)---------------------------
> TIP 9: the planner will ignore your desire to choose an index scan if your
>       joining column's datatypes do not match
>

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

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
Bruce Momjian wrote:
> Is this patch being worked on?

I have shelved it for the moment, waiting on any further comments from
Tom before reworking it.

-O

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Bruce Momjian
Date:
Oliver Jowett wrote:
> Bruce Momjian wrote:
> > Is this patch being worked on?
>
> I have shelved it for the moment, waiting on any further comments from
> Tom before reworking it.

I thought he wanted the patch modified so keepalive could never be
turned off, but that the OS default  parameters could be changed.

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

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Tom Lane
Date:
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> Oliver Jowett wrote:
>> I have shelved it for the moment, waiting on any further comments from
>> Tom before reworking it.

> I thought he wanted the patch modified so keepalive could never be
> turned off, but that the OS default  parameters could be changed.

I wanted it fixed to not do a lot of extra kernel calls during
backend startup...

            regards, tom lane

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
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);

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Bruce Momjian
Date:
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@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Oliver Jowett
Date:
Bruce Momjian wrote:
> Is this the functionality we agreed we wanted?

I think it covers Tom's comments on the first version:

 - a GUC to turn off SO_KEEPALIVE isn't particularly useful
 - don't do redundant get/setsockopt calls on backend startup

-O

Re: Implement support for TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL

From
Bruce Momjian
Date:
Patch applied.  Thanks.

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


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@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073