1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-11 10:01:57 +03:00

Add TCP keepalive support to libpq.

This adds four additional connection parameters to libpq: keepalives,
keepalives_idle, keepalives_count, and keepalives_interval.
keepalives default to on, per discussion, but can be turned off by
specifying keepalives=0.  The remaining parameters, where supported,
can be used to adjust how often keepalives are sent and how many
can be lost before the connection is broken.

The immediate motivation for this patch is to make sure that
walreceiver will eventually notice if the master reboots without
closing the connection cleanly, but it should be helpful in other
cases as well.

Tollef Fog Heen, Fujii Masao, and me.
This commit is contained in:
Robert Haas
2010-06-23 21:54:13 +00:00
parent c1b617987d
commit d8cd283a08
3 changed files with 238 additions and 3 deletions
doc/src/sgml
src/interfaces/libpq

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.393 2010/05/26 21:39:27 tgl Exp $
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.394 2010/06/23 21:54:13 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
@ -183,6 +183,18 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"fallback_application_name", NULL, NULL, NULL,
"Fallback-Application-Name", "", 64},
{"keepalives", NULL, NULL, NULL,
"TCP-Keepalives", "", 1}, /* should be just '0' or '1' */
{"keepalives_idle", NULL, NULL, NULL,
"TCP-Keepalives-Idle", "", 10}, /* strlen(INT32_MAX) == 10 */
{"keepalives_interval", NULL, NULL, NULL,
"TCP-Keepalives-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */
{"keepalives_count", NULL, NULL, NULL,
"TCP-Keepalives-Count", "", 10}, /* strlen(INT32_MAX) == 10 */
#ifdef USE_SSL
/*
@ -552,6 +564,14 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
conn->pgpass = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "connect_timeout");
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives");
conn->keepalives = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_idle");
conn->keepalives_idle = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_interval");
conn->keepalives_interval = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "keepalives_count");
conn->keepalives_count = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslmode");
conn->sslmode = tmp ? strdup(tmp) : NULL;
tmp = conninfo_getval(connOptions, "sslkey");
@ -943,6 +963,119 @@ connectFailureMessage(PGconn *conn, int errorno)
}
}
/*
* Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if
* conn->keepalives is set to a value which is not parseable as an
* integer.
*/
static int
useKeepalives(PGconn *conn)
{
char *ep;
int val;
if (conn->keepalives == NULL)
return 1;
val = strtol(conn->keepalives, &ep, 10);
if (*ep)
return -1;
return val != 0 ? 1 : 0;
}
/*
* Set the keepalive idle timer.
*/
static int
setKeepalivesIdle(PGconn *conn)
{
int idle;
if (conn->keepalives_idle == NULL)
return 1;
idle = atoi(conn->keepalives_idle);
if (idle < 0)
idle = 0;
#ifdef TCP_KEEPIDLE
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPIDLE,
(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;
}
#endif
return 1;
}
/*
* Set the keepalive interval.
*/
static int
setKeepalivesInterval(PGconn *conn)
{
int interval;
if (conn->keepalives_interval == NULL)
return 1;
interval = atoi(conn->keepalives_interval);
if (interval < 0)
interval = 0;
#ifdef TCP_KEEPINTVL
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
(char *) &interval, sizeof(interval)) < 0)
{
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPINTVL) failed: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
#endif
return 1;
}
/*
* Set the count of lost keepalive packets that will trigger a connection
* break.
*/
static int
setKeepalivesCount(PGconn *conn)
{
int count;
if (conn->keepalives_count == NULL)
return 1;
count = atoi(conn->keepalives_count);
if (count < 0)
count = 0;
#ifdef TCP_KEEPCNT
if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT,
(char *) &count, sizeof(count)) < 0)
{
char sebuf[256];
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(TCP_KEEPCNT) failed: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return 0;
}
#endif
return 1;
}
/* ----------
* connectDBStart -
@ -1329,6 +1462,45 @@ keep_going: /* We will come back to here until there is
}
#endif /* F_SETFD */
if (!IS_AF_UNIX(addr_cur->ai_family))
{
int on = 1;
int usekeepalives = useKeepalives(conn);
int err = 0;
if (usekeepalives < 0)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("keepalives parameter must be an integer\n"));
err = 1;
}
else if (usekeepalives == 0)
{
/* Do nothing */
}
else if (setsockopt(conn->sock,
SOL_SOCKET, SO_KEEPALIVE,
(char *) &on, sizeof(on)) < 0)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("setsockopt(SO_KEEPALIVE) failed: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
err = 1;
}
else if (!setKeepalivesIdle(conn)
|| !setKeepalivesInterval(conn)
|| !setKeepalivesCount(conn))
err = 1;
if (err)
{
closesocket(conn->sock);
conn->sock = -1;
conn->addr_cur = addr_cur->ai_next;
continue;
}
}
/*----------
* We have three methods of blocking SIGPIPE during
* send() calls to this socket:
@ -2290,6 +2462,14 @@ freePGconn(PGconn *conn)
free(conn->pguser);
if (conn->pgpass)
free(conn->pgpass);
if (conn->keepalives)
free(conn->keepalives);
if (conn->keepalives_idle)
free(conn->keepalives_idle);
if (conn->keepalives_interval)
free(conn->keepalives_interval);
if (conn->keepalives_count)
free(conn->keepalives_count);
if (conn->sslmode)
free(conn->sslmode);
if (conn->sslcert)