]> granicus.if.org Git - postgresql/commitdiff
Add support for TCP keepalives on Windows, both for backend and the new
authorMagnus Hagander <magnus@hagander.net>
Thu, 8 Jul 2010 10:20:14 +0000 (10:20 +0000)
committerMagnus Hagander <magnus@hagander.net>
Thu, 8 Jul 2010 10:20:14 +0000 (10:20 +0000)
libpq support.

doc/src/sgml/config.sgml
doc/src/sgml/libpq.sgml
src/backend/libpq/pqcomm.c
src/interfaces/libpq/fe-connect.c

index e54b09545d0a362fbb71dec90599c8b12abc57cc..2fbe6a8c24b460d6a07944ef8dcc80765091a7bf 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.293 2010/07/06 22:55:26 rhaas Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.294 2010/07/08 10:20:13 mha Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -523,12 +523,17 @@ SET ENABLE_SEQSCAN TO OFF;
       </indexterm>
       <listitem>
        <para>
-        On systems that support the <symbol>TCP_KEEPIDLE</symbol> or
-        <symbol>TCP_KEEPALIVE</> socket option, specifies the
-        number of seconds between sending keepalives on an otherwise idle
-        connection. A value of zero uses the system default. If neither of
-        these socket options is supported, this parameter must be zero. This
-        parameter is ignored for connections made via a Unix-domain socket.
+        Specifies the number of seconds before sending a keepalive packet on an otherwise idle
+        connection.  A value of 0 uses the system default.  This parameter is supported
+        only on systems that support the <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
+        symbols, and on Windows; on other systems, it must be zero. This parameter is
+        ignored for connections made via a Unix-domain socket.
+         <note>
+          <para>
+           On Windows, a value of 0 will set this parameter to 2 hours,
+           since Windows does not provide a way to read the default value.
+          </para>
+         </note>
        </para>
       </listitem>
      </varlistentry>
@@ -540,11 +545,17 @@ SET ENABLE_SEQSCAN TO OFF;
       </indexterm>
       <listitem>
        <para>
-        On systems that support the <symbol>TCP_KEEPINTVL</symbol> socket option, specifies how
-        long, in seconds, to wait for a response to a keepalive before
-        retransmitting. A value of zero uses the system default. If <symbol>TCP_KEEPINTVL</symbol>
-        is not supported, this parameter must be zero. This parameter is ignored
-        for connections made via a Unix-domain socket.
+        Specifies the number of seconds between sending keepalives on an otherwise idle
+        connection.  A value of 0 uses the system default.  This parameter is supported
+        only on systems that support the <symbol>TCP_KEEPINTVL</>
+        symbol, and on Windows; on other systems, it must be zero. This parameter is
+        ignored for connections made via a Unix-domain socket.
+         <note>
+          <para>
+           On Windows, a value of 0 will set this parameter to 1 second,
+           since Windows does not provide a way to read the default value.
+          </para>
+         </note>
        </para>
       </listitem>
      </varlistentry>
@@ -556,11 +567,16 @@ SET ENABLE_SEQSCAN TO OFF;
       </indexterm>
       <listitem>
        <para>
-        On systems that support the <symbol>TCP_KEEPCNT</symbol> socket option, specifies how
-        many keepalives can be lost before the connection is considered dead.
-        A value of zero uses the system default. If <symbol>TCP_KEEPCNT</symbol> is not
-        supported, this parameter must be zero. This parameter is ignored
-        for connections made via a Unix-domain socket.
+        Specifies the number of keepalive packets to send on an otherwise idle
+        connection.  A value of 0 uses the system default.  This parameter is supported
+        only on systems that support the <symbol>TCP_KEEPCNT</>
+        symbol; on other systems, it must be zero. This parameter is
+        ignored for connections made via a Unix-domain socket.
+         <note>
+          <para>
+           This parameter is not supported on Windows, and must be zero.
+          </para>
+         </note>
        </para>
       </listitem>
      </varlistentry>
index f70541bb7f1584c5e27d424f260d39414586095f..9e047b110d72eaec2033468cc0b38efc9cb91bd9 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.312 2010/07/06 21:14:25 rhaas Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.313 2010/07/08 10:20:14 mha Exp $ -->
 
 <chapter id="libpq">
  <title><application>libpq</application> - C Library</title>
           <para>
            Controls the number of seconds of inactivity after which TCP should
            send a keepalive message to the server.  A value of zero uses the
-           system default.  This parameter is ignored if the neither the
-           <symbol>TCP_KEEPIDLE</> nor the <symbol>TCP_KEEPALIVE</> socket
-           options are supported, for connections made via a Unix-domain
-           socket, or if keepalives are disabled.
+           system default. This parameter is ignored for connections made via a
+           Unix-domain socket, or if keepalives are disabled. It is only supported
+           on systems where the <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
+           socket option is available, and on Windows; on other systems, it has no
+           effect.
           </para>
          </listitem>
         </varlistentry>
           <para>
            Controls the number of seconds after which a TCP keepalive message
            that is not acknowledged by the server should be retransmitted.  A
-           value of zero uses the system default.  This parameter is ignored if
-           the <symbol>TCP_KEEPINTVL</> socket option is not supported, for
-           connections made via a Unix-domain socket, or if keepalives are
-           disabled.
+           value of zero uses the system default. This parameter is ignored for
+           connections made via a Unix-domain socket, or if keepalives are disabled.
+           It is only supported on systems where the <symbol>TCP_KEEPINTVL</>
+           socket option is available, and on Windows; on other systems, it has no
+           effect.
           </para>
          </listitem>
         </varlistentry>
           <para>
            Controls the number of TCP keepalives that can be lost before the
            client's connection to the server is considered dead.  A value of
-           zero uses the system default.  This parameter is ignored if the
-           <symbol>TCP_KEEPCNT</> socket option is not supported, for
-           connections made via a Unix-domain socket, or if keepalives are
-           disabled.
+           zero uses the system default. This parameter is ignored for
+           connections made via a Unix-domain socket, or if keepalives are disabled.
+           It is only supported on systems where the <symbol>TCP_KEEPINTVL</>
+           socket option is available; on other systems, it has no effect.
           </para>
          </listitem>
         </varlistentry>
index 46577f379ac969bd2e32df85dd1ba254c1fae577..b5be4baa444435b95efb1b371bc0085e1eb54bdd 100644 (file)
@@ -30,7 +30,7 @@
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.210 2010/07/06 21:14:25 rhaas Exp $
+ *     $PostgreSQL: pgsql/src/backend/libpq/pqcomm.c,v 1.211 2010/07/08 10:20:12 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,6 +83,9 @@
 #ifdef HAVE_UTIME_H
 #include <utime.h>
 #endif
+#ifdef WIN32
+#include <mstcpip.h>
+#endif
 
 #include "libpq/ip.h"
 #include "libpq/libpq.h"
@@ -1314,10 +1317,55 @@ pq_endcopyout(bool errorAbort)
  * Support for TCP Keepalive parameters
  */
 
+/*
+ * On Windows, we need to set both idle and interval at the same time.
+ * We also cannot reset them to the default (setting to zero will
+ * actually set them to zero, not default), therefor we fallback to
+ * the out-of-the-box default instead.
+ */
+#ifdef WIN32
+static int
+pq_setkeepaliveswin32(Port *port, int idle, int interval)
+{
+       struct tcp_keepalive    ka;
+       DWORD                                   retsize;
+
+       if (idle <= 0)
+               idle = 2 * 60 * 60; /* default = 2 hours */
+       if (interval <= 0)
+               interval = 1;       /* default = 1 second */
+
+       ka.onoff = 1;
+       ka.keepalivetime = idle * 1000;
+       ka.keepaliveinterval = interval * 1000;
+
+       if (WSAIoctl(port->sock,
+                                SIO_KEEPALIVE_VALS,
+                                (LPVOID) &ka,
+                                sizeof(ka),
+                                NULL,
+                                0,
+                                &retsize,
+                                NULL,
+                                NULL)
+               != 0)
+       {
+               elog(LOG, "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui",
+                        WSAGetLastError());
+               return STATUS_ERROR;
+       }
+       if (port->keepalives_idle != idle)
+               port->keepalives_idle = idle;
+       if (port->keepalives_interval != interval)
+               port->keepalives_interval = interval;
+       return STATUS_OK;
+}
+#endif
+
 int
 pq_getkeepalivesidle(Port *port)
 {
-#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
+#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE) || defined(WIN32)
        if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
                return 0;
 
@@ -1326,6 +1374,7 @@ pq_getkeepalivesidle(Port *port)
 
        if (port->default_keepalives_idle == 0)
        {
+#ifndef WIN32
                ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_idle);
 
 #ifdef TCP_KEEPIDLE
@@ -1344,7 +1393,11 @@ pq_getkeepalivesidle(Port *port)
                        elog(LOG, "getsockopt(TCP_KEEPALIVE) failed: %m");
                        port->default_keepalives_idle = -1; /* don't know */
                }
-#endif
+#endif /* TCP_KEEPIDLE */
+#else /* WIN32 */
+               /* We can't get the defaults on Windows, so return "don't know" */
+               port->default_keepalives_idle = -1;
+#endif /* WIN32 */
        }
 
        return port->default_keepalives_idle;
@@ -1359,10 +1412,11 @@ pq_setkeepalivesidle(int idle, Port *port)
        if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
                return STATUS_OK;
 
-#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE)
+#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE) || defined(WIN32)
        if (idle == port->keepalives_idle)
                return STATUS_OK;
 
+#ifndef WIN32
        if (port->default_keepalives_idle <= 0)
        {
                if (pq_getkeepalivesidle(port) < 0)
@@ -1394,21 +1448,23 @@ pq_setkeepalivesidle(int idle, Port *port)
 #endif
 
        port->keepalives_idle = idle;
-#else
+#else /* WIN32 */
+       return pq_setkeepaliveswin32(port, idle, port->keepalives_interval);
+#endif
+#else /* TCP_KEEPIDLE || WIN32 */
        if (idle != 0)
        {
                elog(LOG, "setting the keepalive idle time is not supported");
                return STATUS_ERROR;
        }
 #endif
-
        return STATUS_OK;
 }
 
 int
 pq_getkeepalivesinterval(Port *port)
 {
-#ifdef TCP_KEEPINTVL
+#if defined(TCP_KEEPINTVL) || defined(WIN32)
        if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
                return 0;
 
@@ -1417,6 +1473,7 @@ pq_getkeepalivesinterval(Port *port)
 
        if (port->default_keepalives_interval == 0)
        {
+#ifndef WIN32
                ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_interval);
 
                if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPINTVL,
@@ -1426,6 +1483,10 @@ pq_getkeepalivesinterval(Port *port)
                        elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
                        port->default_keepalives_interval = -1;         /* don't know */
                }
+#else
+               /* We can't get the defaults on Windows, so return "don't know" */
+               port->default_keepalives_interval = -1;
+#endif /* WIN32 */
        }
 
        return port->default_keepalives_interval;
@@ -1440,10 +1501,11 @@ pq_setkeepalivesinterval(int interval, Port *port)
        if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
                return STATUS_OK;
 
-#ifdef TCP_KEEPINTVL
+#if defined(TCP_KEEPINTVL) || defined (WIN32)
        if (interval == port->keepalives_interval)
                return STATUS_OK;
 
+#ifndef WIN32
        if (port->default_keepalives_interval <= 0)
        {
                if (pq_getkeepalivesinterval(port) < 0)
@@ -1466,6 +1528,9 @@ pq_setkeepalivesinterval(int interval, Port *port)
        }
 
        port->keepalives_interval = interval;
+#else /* WIN32 */
+       return pq_setkeepaliveswin32(port, port->keepalives_idle, interval);
+#endif
 #else
        if (interval != 0)
        {
index 5bb404fc27ebd1350675048b787a1f4f3ca26138..5f671f3299319cafaa23bce11fedbaca3ebe9521 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.396 2010/07/06 21:14:25 rhaas Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.397 2010/07/08 10:20:12 mha Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #endif
 #define near
 #include <shlobj.h>
+#include <mstcpip.h>
 #else
 #include <sys/socket.h>
 #include <netdb.h>
@@ -982,6 +983,7 @@ useKeepalives(PGconn *conn)
        return val != 0 ? 1 : 0;
 }
 
+#ifndef WIN32
 /*
  * Set the keepalive idle timer.
  */
@@ -1090,6 +1092,52 @@ setKeepalivesCount(PGconn *conn)
        return 1;
 }
 
+#else /* Win32 */
+/*
+ * Enable keepalives and set the keepalive values on Win32,
+ * where they are always set in one batch.
+ */
+static int
+setKeepalivesWin32(PGconn *conn)
+{
+       struct tcp_keepalive    ka;
+       DWORD                                   retsize;
+       int                                             idle = 0;
+       int                                             interval = 0;
+
+       if (conn->keepalives_idle)
+               idle = atoi(conn->keepalives_idle);
+       if (idle <= 0)
+               idle = 2 * 60 * 60; /* 2 hours = default */
+
+       if (conn->keepalives_interval)
+               interval = atoi(conn->keepalives_interval);
+       if (interval <= 0)
+               interval = 1; /* 1 second = default */
+
+       ka.onoff = 1;
+       ka.keepalivetime = idle * 1000;
+       ka.keepaliveinterval = interval * 1000;
+
+       if (WSAIoctl(conn->sock,
+                                SIO_KEEPALIVE_VALS,
+                                (LPVOID) &ka,
+                                sizeof(ka),
+                                NULL,
+                                0,
+                                &retsize,
+                                NULL,
+                                NULL)
+               != 0)
+       {
+               appendPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"),
+                                                 WSAGetLastError());
+               return 0;
+       }
+       return 1;
+}
+#endif /* WIN32 */
 
 /* ----------
  * connectDBStart -
@@ -1492,6 +1540,7 @@ keep_going:                                               /* We will come back to here until there is
                                                {
                                                        /* Do nothing */
                                                }
+#ifndef WIN32
                                                else if (setsockopt(conn->sock,
                                                                                        SOL_SOCKET, SO_KEEPALIVE,
                                                                                        (char *) &on, sizeof(on)) < 0)
@@ -1505,6 +1554,10 @@ keep_going:                                              /* We will come back to here until there is
                                                                 || !setKeepalivesInterval(conn)
                                                                 || !setKeepalivesCount(conn))
                                                        err = 1;
+#else /* WIN32 */
+                                               else if (!setKeepalivesWin32(conn))
+                                                       err = 1;
+#endif /* WIN32 */
 
                                                if (err)
                                                {