Add support for TCP keepalives on Windows, both for backend and the new

libpq support.
This commit is contained in:
Magnus Hagander 2010-07-08 10:20:14 +00:00
parent d4d32eefdf
commit 44b0d1671a
4 changed files with 175 additions and 39 deletions

View 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"> <chapter Id="runtime-config">
<title>Server Configuration</title> <title>Server Configuration</title>
@ -523,12 +523,17 @@ SET ENABLE_SEQSCAN TO OFF;
</indexterm> </indexterm>
<listitem> <listitem>
<para> <para>
On systems that support the <symbol>TCP_KEEPIDLE</symbol> or Specifies the number of seconds before sending a keepalive packet on an otherwise idle
<symbol>TCP_KEEPALIVE</> socket option, specifies the connection. A value of 0 uses the system default. This parameter is supported
number of seconds between sending keepalives on an otherwise idle only on systems that support the <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
connection. A value of zero uses the system default. If neither of symbols, and on Windows; on other systems, it must be zero. This parameter is
these socket options is supported, this parameter must be zero. This ignored for connections made via a Unix-domain socket.
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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -540,11 +545,17 @@ SET ENABLE_SEQSCAN TO OFF;
</indexterm> </indexterm>
<listitem> <listitem>
<para> <para>
On systems that support the <symbol>TCP_KEEPINTVL</symbol> socket option, specifies how Specifies the number of seconds between sending keepalives on an otherwise idle
long, in seconds, to wait for a response to a keepalive before connection. A value of 0 uses the system default. This parameter is supported
retransmitting. A value of zero uses the system default. If <symbol>TCP_KEEPINTVL</symbol> only on systems that support the <symbol>TCP_KEEPINTVL</>
is not supported, this parameter must be zero. This parameter is ignored symbol, and on Windows; on other systems, it must be zero. This parameter is
for connections made via a Unix-domain socket. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -556,11 +567,16 @@ SET ENABLE_SEQSCAN TO OFF;
</indexterm> </indexterm>
<listitem> <listitem>
<para> <para>
On systems that support the <symbol>TCP_KEEPCNT</symbol> socket option, specifies how Specifies the number of keepalive packets to send on an otherwise idle
many keepalives can be lost before the connection is considered dead. connection. A value of 0 uses the system default. This parameter is supported
A value of zero uses the system default. If <symbol>TCP_KEEPCNT</symbol> is not only on systems that support the <symbol>TCP_KEEPCNT</>
supported, this parameter must be zero. This parameter is ignored symbol; on other systems, it must be zero. This parameter is
for connections made via a Unix-domain socket. 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View 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"> <chapter id="libpq">
<title><application>libpq</application> - C Library</title> <title><application>libpq</application> - C Library</title>
@ -298,10 +298,11 @@
<para> <para>
Controls the number of seconds of inactivity after which TCP should Controls the number of seconds of inactivity after which TCP should
send a keepalive message to the server. A value of zero uses the send a keepalive message to the server. A value of zero uses the
system default. This parameter is ignored if the neither the system default. This parameter is ignored for connections made via a
<symbol>TCP_KEEPIDLE</> nor the <symbol>TCP_KEEPALIVE</> socket Unix-domain socket, or if keepalives are disabled. It is only supported
options are supported, for connections made via a Unix-domain on systems where the <symbol>TCP_KEEPIDLE</> or <symbol>TCP_KEEPALIVE</>
socket, or if keepalives are disabled. socket option is available, and on Windows; on other systems, it has no
effect.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -312,10 +313,11 @@
<para> <para>
Controls the number of seconds after which a TCP keepalive message Controls the number of seconds after which a TCP keepalive message
that is not acknowledged by the server should be retransmitted. A that is not acknowledged by the server should be retransmitted. A
value of zero uses the system default. This parameter is ignored if value of zero uses the system default. This parameter is ignored for
the <symbol>TCP_KEEPINTVL</> socket option is not supported, for connections made via a Unix-domain socket, or if keepalives are disabled.
connections made via a Unix-domain socket, or if keepalives are It is only supported on systems where the <symbol>TCP_KEEPINTVL</>
disabled. socket option is available, and on Windows; on other systems, it has no
effect.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -326,10 +328,10 @@
<para> <para>
Controls the number of TCP keepalives that can be lost before the Controls the number of TCP keepalives that can be lost before the
client's connection to the server is considered dead. A value of client's connection to the server is considered dead. A value of
zero uses the system default. This parameter is ignored if the zero uses the system default. This parameter is ignored for
<symbol>TCP_KEEPCNT</> socket option is not supported, for connections made via a Unix-domain socket, or if keepalives are disabled.
connections made via a Unix-domain socket, or if keepalives are It is only supported on systems where the <symbol>TCP_KEEPINTVL</>
disabled. socket option is available; on other systems, it has no effect.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 #ifdef HAVE_UTIME_H
#include <utime.h> #include <utime.h>
#endif #endif
#ifdef WIN32
#include <mstcpip.h>
#endif
#include "libpq/ip.h" #include "libpq/ip.h"
#include "libpq/libpq.h" #include "libpq/libpq.h"
@ -1314,10 +1317,55 @@ pq_endcopyout(bool errorAbort)
* Support for TCP Keepalive parameters * 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 int
pq_getkeepalivesidle(Port *port) 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)) if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
return 0; return 0;
@ -1326,6 +1374,7 @@ pq_getkeepalivesidle(Port *port)
if (port->default_keepalives_idle == 0) if (port->default_keepalives_idle == 0)
{ {
#ifndef WIN32
ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_idle); ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_idle);
#ifdef TCP_KEEPIDLE #ifdef TCP_KEEPIDLE
@ -1344,7 +1393,11 @@ pq_getkeepalivesidle(Port *port)
elog(LOG, "getsockopt(TCP_KEEPALIVE) failed: %m"); elog(LOG, "getsockopt(TCP_KEEPALIVE) failed: %m");
port->default_keepalives_idle = -1; /* don't know */ 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; 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)) if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
return STATUS_OK; return STATUS_OK;
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE) #if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE) || defined(WIN32)
if (idle == port->keepalives_idle) if (idle == port->keepalives_idle)
return STATUS_OK; return STATUS_OK;
#ifndef WIN32
if (port->default_keepalives_idle <= 0) if (port->default_keepalives_idle <= 0)
{ {
if (pq_getkeepalivesidle(port) < 0) if (pq_getkeepalivesidle(port) < 0)
@ -1394,21 +1448,23 @@ pq_setkeepalivesidle(int idle, Port *port)
#endif #endif
port->keepalives_idle = idle; port->keepalives_idle = idle;
#else #else /* WIN32 */
return pq_setkeepaliveswin32(port, idle, port->keepalives_interval);
#endif
#else /* TCP_KEEPIDLE || WIN32 */
if (idle != 0) if (idle != 0)
{ {
elog(LOG, "setting the keepalive idle time is not supported"); elog(LOG, "setting the keepalive idle time is not supported");
return STATUS_ERROR; return STATUS_ERROR;
} }
#endif #endif
return STATUS_OK; return STATUS_OK;
} }
int int
pq_getkeepalivesinterval(Port *port) pq_getkeepalivesinterval(Port *port)
{ {
#ifdef TCP_KEEPINTVL #if defined(TCP_KEEPINTVL) || defined(WIN32)
if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family)) if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
return 0; return 0;
@ -1417,6 +1473,7 @@ pq_getkeepalivesinterval(Port *port)
if (port->default_keepalives_interval == 0) if (port->default_keepalives_interval == 0)
{ {
#ifndef WIN32
ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_interval); ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_interval);
if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPINTVL, if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPINTVL,
@ -1426,6 +1483,10 @@ pq_getkeepalivesinterval(Port *port)
elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m"); elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
port->default_keepalives_interval = -1; /* don't know */ 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; 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)) if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
return STATUS_OK; return STATUS_OK;
#ifdef TCP_KEEPINTVL #if defined(TCP_KEEPINTVL) || defined (WIN32)
if (interval == port->keepalives_interval) if (interval == port->keepalives_interval)
return STATUS_OK; return STATUS_OK;
#ifndef WIN32
if (port->default_keepalives_interval <= 0) if (port->default_keepalives_interval <= 0)
{ {
if (pq_getkeepalivesinterval(port) < 0) if (pq_getkeepalivesinterval(port) < 0)
@ -1466,6 +1528,9 @@ pq_setkeepalivesinterval(int interval, Port *port)
} }
port->keepalives_interval = interval; port->keepalives_interval = interval;
#else /* WIN32 */
return pq_setkeepaliveswin32(port, port->keepalives_idle, interval);
#endif
#else #else
if (interval != 0) if (interval != 0)
{ {

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 #endif
#define near #define near
#include <shlobj.h> #include <shlobj.h>
#include <mstcpip.h>
#else #else
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
@ -982,6 +983,7 @@ useKeepalives(PGconn *conn)
return val != 0 ? 1 : 0; return val != 0 ? 1 : 0;
} }
#ifndef WIN32
/* /*
* Set the keepalive idle timer. * Set the keepalive idle timer.
*/ */
@ -1090,6 +1092,52 @@ setKeepalivesCount(PGconn *conn)
return 1; 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 - * connectDBStart -
@ -1492,6 +1540,7 @@ keep_going: /* We will come back to here until there is
{ {
/* Do nothing */ /* Do nothing */
} }
#ifndef WIN32
else if (setsockopt(conn->sock, else if (setsockopt(conn->sock,
SOL_SOCKET, SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof(on)) < 0) (char *) &on, sizeof(on)) < 0)
@ -1505,6 +1554,10 @@ keep_going: /* We will come back to here until there is
|| !setKeepalivesInterval(conn) || !setKeepalivesInterval(conn)
|| !setKeepalivesCount(conn)) || !setKeepalivesCount(conn))
err = 1; err = 1;
#else /* WIN32 */
else if (!setKeepalivesWin32(conn))
err = 1;
#endif /* WIN32 */
if (err) if (err)
{ {