Second try at fixing tcp_keepalives_idle option on Solaris.

Buildfarm evidence shows that TCP_KEEPALIVE_THRESHOLD doesn't exist
after all on Solaris < 11.  This means we need to take positive action to
prevent the TCP_KEEPALIVE code path from being taken on that platform.
I've chosen to limit it with "&& defined(__darwin__)", since it's unclear
that anyone else would follow Apple's precedent of spelling the symbol
that way.

Also, follow a suggestion from Michael Paquier of eliminating code
duplication by defining a couple of intermediate symbols for the
socket option.

In passing, make some effort to reduce the number of translatable messages
by replacing "setsockopt(foo) failed" with "setsockopt(%s) failed", etc,
throughout the affected files.  And update relevant documentation so
that it doesn't claim to provide an exhaustive list of the possible
socket option names.

Like the previous commit (f0256c774), back-patch to all supported branches.

Discussion: https://postgr.es/m/20170627163757.25161.528@wrigleys.postgresql.org
This commit is contained in:
Tom Lane 2017-06-28 12:30:16 -04:00
parent 4500edc7e9
commit 99255d73c0
4 changed files with 86 additions and 106 deletions

View File

@ -856,8 +856,8 @@ include_dir 'conf.d'
Specifies the number of seconds of inactivity after which TCP
should send a keepalive message to the client. 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
This parameter is supported only on systems that support
<symbol>TCP_KEEPIDLE</> or an equivalent socket option, and on
Windows; on other systems, it must be zero.
In sessions connected via a Unix-domain socket, this parameter is
ignored and always reads as zero.
@ -882,9 +882,9 @@ include_dir 'conf.d'
Specifies the number of seconds after which a TCP keepalive message
that is not acknowledged by the client should be retransmitted.
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 supported only on systems that support
<symbol>TCP_KEEPINTVL</> or an equivalent socket option, and on
Windows; on other systems, it must be zero.
In sessions connected via a Unix-domain socket, this parameter is
ignored and always reads as zero.
</para>
@ -906,10 +906,11 @@ include_dir 'conf.d'
<listitem>
<para>
Specifies the number of TCP keepalives that can be lost before
the server's connection to the client is considered dead. 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.
the server's connection to the client is considered dead.
A value of 0 uses the system default.
This parameter is supported only on systems that support
<symbol>TCP_KEEPCNT</> or an equivalent socket option;
on other systems, it must be zero.
In sessions connected via a Unix-domain socket, this parameter is
ignored and always reads as zero.
</para>

View File

@ -1125,10 +1125,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
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 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.
Unix-domain socket, or if keepalives are disabled.
It is only supported on systems where <symbol>TCP_KEEPIDLE</> or
an equivalent socket option is available, and on Windows; on other
systems, it has no effect.
</para>
</listitem>
</varlistentry>
@ -1141,9 +1141,9 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
that is not acknowledged by the server should be retransmitted. A
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.
It is only supported on systems where <symbol>TCP_KEEPINTVL</> or
an equivalent socket option is available, and on Windows; on other
systems, it has no effect.
</para>
</listitem>
</varlistentry>
@ -1156,8 +1156,9 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
client's connection to the server is considered dead. A 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_KEEPCNT</>
socket option is available; on other systems, it has no effect.
It is only supported on systems where <symbol>TCP_KEEPCNT</> or
an equivalent socket option is available; on other systems, it has no
effect.
</para>
</listitem>
</varlistentry>

View File

@ -96,6 +96,25 @@
#include "utils/guc.h"
#include "utils/memutils.h"
/*
* Cope with the various platform-specific ways to spell TCP keepalive socket
* options. This doesn't cover Windows, which as usual does its own thing.
*/
#if defined(TCP_KEEPIDLE)
/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE"
#elif defined(TCP_KEEPALIVE_THRESHOLD)
/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD"
#elif defined(TCP_KEEPALIVE) && defined(__darwin__)
/* TCP_KEEPALIVE is the name of this option on macOS */
/* Caution: Solaris has this symbol but it means something different */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE"
#endif
/*
* Configuration options
*/
@ -742,7 +761,7 @@ StreamConnection(pgsocket server_fd, Port *port)
if (setsockopt(port->sock, IPPROTO_TCP, TCP_NODELAY,
(char *) &on, sizeof(on)) < 0)
{
elog(LOG, "setsockopt(TCP_NODELAY) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", "TCP_NODELAY");
return STATUS_ERROR;
}
#endif
@ -750,7 +769,7 @@ StreamConnection(pgsocket server_fd, Port *port)
if (setsockopt(port->sock, SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof(on)) < 0)
{
elog(LOG, "setsockopt(SO_KEEPALIVE) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", "SO_KEEPALIVE");
return STATUS_ERROR;
}
@ -781,7 +800,7 @@ StreamConnection(pgsocket server_fd, Port *port)
if (getsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &oldopt,
&optlen) < 0)
{
elog(LOG, "getsockopt(SO_SNDBUF) failed: %m");
elog(LOG, "getsockopt(%s) failed: %m", "SO_SNDBUF");
return STATUS_ERROR;
}
newopt = PQ_SEND_BUFFER_SIZE * 4;
@ -790,7 +809,7 @@ StreamConnection(pgsocket server_fd, Port *port)
if (setsockopt(port->sock, SOL_SOCKET, SO_SNDBUF, (char *) &newopt,
sizeof(newopt)) < 0)
{
elog(LOG, "setsockopt(SO_SNDBUF) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", "SO_SNDBUF");
return STATUS_ERROR;
}
}
@ -1676,7 +1695,7 @@ pq_setkeepaliveswin32(Port *port, int idle, int interval)
int
pq_getkeepalivesidle(Port *port)
{
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE_THRESHOLD) || defined(TCP_KEEPALIVE) || defined(WIN32)
#if defined(PG_TCP_KEEPALIVE_IDLE) || defined(SIO_KEEPALIVE_VALS)
if (port == NULL || IS_AF_UNIX(port->laddr.addr.ss_family))
return 0;
@ -1688,34 +1707,13 @@ pq_getkeepalivesidle(Port *port)
#ifndef WIN32
ACCEPT_TYPE_ARG3 size = sizeof(port->default_keepalives_idle);
#if defined(TCP_KEEPIDLE)
/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPIDLE,
if (getsockopt(port->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
(char *) &port->default_keepalives_idle,
&size) < 0)
{
elog(LOG, "getsockopt(TCP_KEEPIDLE) failed: %m");
elog(LOG, "getsockopt(%s) failed: %m", PG_TCP_KEEPALIVE_IDLE_STR);
port->default_keepalives_idle = -1; /* don't know */
}
#elif defined(TCP_KEEPALIVE_THRESHOLD)
/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris */
if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
(char *) &port->default_keepalives_idle,
&size) < 0)
{
elog(LOG, "getsockopt(TCP_KEEPALIVE_THRESHOLD) failed: %m");
port->default_keepalives_idle = -1; /* don't know */
}
#else /* must have TCP_KEEPALIVE */
/* TCP_KEEPALIVE is the name of this option on macOS */
if (getsockopt(port->sock, IPPROTO_TCP, TCP_KEEPALIVE,
(char *) &port->default_keepalives_idle,
&size) < 0)
{
elog(LOG, "getsockopt(TCP_KEEPALIVE) failed: %m");
port->default_keepalives_idle = -1; /* don't know */
}
#endif /* KEEPIDLE/KEEPALIVE_THRESHOLD/KEEPALIVE */
#else /* WIN32 */
/* We can't get the defaults on Windows, so return "don't know" */
port->default_keepalives_idle = -1;
@ -1735,7 +1733,7 @@ pq_setkeepalivesidle(int idle, Port *port)
return STATUS_OK;
/* check SIO_KEEPALIVE_VALS here, not just WIN32, as some toolchains lack it */
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPALIVE_THRESHOLD) || defined(TCP_KEEPALIVE) || defined(SIO_KEEPALIVE_VALS)
#if defined(PG_TCP_KEEPALIVE_IDLE) || defined(SIO_KEEPALIVE_VALS)
if (idle == port->keepalives_idle)
return STATUS_OK;
@ -1754,43 +1752,25 @@ pq_setkeepalivesidle(int idle, Port *port)
if (idle == 0)
idle = port->default_keepalives_idle;
#if defined(TCP_KEEPIDLE)
/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
if (setsockopt(port->sock, IPPROTO_TCP, TCP_KEEPIDLE,
if (setsockopt(port->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
(char *) &idle, sizeof(idle)) < 0)
{
elog(LOG, "setsockopt(TCP_KEEPIDLE) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", PG_TCP_KEEPALIVE_IDLE_STR);
return STATUS_ERROR;
}
#elif defined(TCP_KEEPALIVE_THRESHOLD)
/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris */
if (setsockopt(port->sock, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
(char *) &idle, sizeof(idle)) < 0)
{
elog(LOG, "setsockopt(TCP_KEEPALIVE_THRESHOLD) failed: %m");
return STATUS_ERROR;
}
#else /* must have TCP_KEEPALIVE */
/* TCP_KEEPALIVE is the name of this option on macOS */
if (setsockopt(port->sock, IPPROTO_TCP, TCP_KEEPALIVE,
(char *) &idle, sizeof(idle)) < 0)
{
elog(LOG, "setsockopt(TCP_KEEPALIVE) failed: %m");
return STATUS_ERROR;
}
#endif
port->keepalives_idle = idle;
#else /* WIN32 */
return pq_setkeepaliveswin32(port, idle, port->keepalives_interval);
#endif
#else /* no way to set it */
#else
if (idle != 0)
{
elog(LOG, "setting the keepalive idle time is not supported");
return STATUS_ERROR;
}
#endif
return STATUS_OK;
}
@ -1813,7 +1793,7 @@ pq_getkeepalivesinterval(Port *port)
(char *) &port->default_keepalives_interval,
&size) < 0)
{
elog(LOG, "getsockopt(TCP_KEEPINTVL) failed: %m");
elog(LOG, "getsockopt(%s) failed: %m", "TCP_KEEPINTVL");
port->default_keepalives_interval = -1; /* don't know */
}
#else
@ -1856,7 +1836,7 @@ pq_setkeepalivesinterval(int interval, Port *port)
if (setsockopt(port->sock, IPPROTO_TCP, TCP_KEEPINTVL,
(char *) &interval, sizeof(interval)) < 0)
{
elog(LOG, "setsockopt(TCP_KEEPINTVL) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", "TCP_KEEPINTVL");
return STATUS_ERROR;
}
@ -1867,7 +1847,7 @@ pq_setkeepalivesinterval(int interval, Port *port)
#else
if (interval != 0)
{
elog(LOG, "setsockopt(TCP_KEEPINTVL) not supported");
elog(LOG, "setsockopt(%s) not supported", "TCP_KEEPINTVL");
return STATUS_ERROR;
}
#endif
@ -1893,7 +1873,7 @@ pq_getkeepalivescount(Port *port)
(char *) &port->default_keepalives_count,
&size) < 0)
{
elog(LOG, "getsockopt(TCP_KEEPCNT) failed: %m");
elog(LOG, "getsockopt(%s) failed: %m", "TCP_KEEPCNT");
port->default_keepalives_count = -1; /* don't know */
}
}
@ -1931,7 +1911,7 @@ pq_setkeepalivescount(int count, Port *port)
if (setsockopt(port->sock, IPPROTO_TCP, TCP_KEEPCNT,
(char *) &count, sizeof(count)) < 0)
{
elog(LOG, "setsockopt(TCP_KEEPCNT) failed: %m");
elog(LOG, "setsockopt(%s) failed: %m", "TCP_KEEPCNT");
return STATUS_ERROR;
}
@ -1939,7 +1919,7 @@ pq_setkeepalivescount(int count, Port *port)
#else
if (count != 0)
{
elog(LOG, "setsockopt(TCP_KEEPCNT) not supported");
elog(LOG, "setsockopt(%s) not supported", "TCP_KEEPCNT");
return STATUS_ERROR;
}
#endif

View File

@ -94,6 +94,25 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
/* This too */
#define ERRCODE_CANNOT_CONNECT_NOW "57P03"
/*
* Cope with the various platform-specific ways to spell TCP keepalive socket
* options. This doesn't cover Windows, which as usual does its own thing.
*/
#if defined(TCP_KEEPIDLE)
/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE"
#elif defined(TCP_KEEPALIVE_THRESHOLD)
/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD"
#elif defined(TCP_KEEPALIVE) && defined(__darwin__)
/* TCP_KEEPALIVE is the name of this option on macOS */
/* Caution: Solaris has this symbol but it means something different */
#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE
#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE"
#endif
/*
* fall back options if they are not specified by arguments or defined
* by environment variables
@ -1470,39 +1489,15 @@ setKeepalivesIdle(PGconn *conn)
if (idle < 0)
idle = 0;
#if defined(TCP_KEEPIDLE)
/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPIDLE,
#ifdef PG_TCP_KEEPALIVE_IDLE
if (setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
(char *) &idle, sizeof(idle)) < 0)
{
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPIDLE) failed: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
#elif defined(TCP_KEEPALIVE_THRESHOLD)
/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris */
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
(char *) &idle, sizeof(idle)) < 0)
{
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPALIVE_THRESHOLD) failed: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
#elif defined(TCP_KEEPALIVE)
/* TCP_KEEPALIVE is the name of this option on macOS */
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPALIVE,
(char *) &idle, sizeof(idle)) < 0)
{
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPALIVE) failed: %s\n"),
libpq_gettext("setsockopt(%s) failed: %s\n"),
PG_TCP_KEEPALIVE_IDLE_STR,
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
@ -1533,7 +1528,8 @@ setKeepalivesInterval(PGconn *conn)
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPINTVL) failed: %s\n"),
libpq_gettext("setsockopt(%s) failed: %s\n"),
"TCP_KEEPINTVL",
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
@ -1565,7 +1561,8 @@ setKeepalivesCount(PGconn *conn)
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPCNT) failed: %s\n"),
libpq_gettext("setsockopt(%s) failed: %s\n"),
"TCP_KEEPCNT",
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
@ -2123,7 +2120,8 @@ keep_going: /* We will come back to here until there is
(char *) &on, sizeof(on)) < 0)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(SO_KEEPALIVE) failed: %s\n"),
libpq_gettext("setsockopt(%s) failed: %s\n"),
"SO_KEEPALIVE",
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
err = 1;
}