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">
<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>

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">
<title><application>libpq</application> - C Library</title>
@ -298,10 +298,11 @@
<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>
@ -312,10 +313,11 @@
<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>
@ -326,10 +328,10 @@
<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>

View 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)
{

View 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)
{