On Tue, Feb 9, 2010 at 11:34 PM, Andrew Chernow <a...@esilo.com> wrote:
> If you really want libpq to manage this, I think you need to expose the
> probe interval and timeouts.

Agreed.

Previously I was making the patch that exposes them as conninfo
options so that the standby can detect a network outage ASAP in SR.
I attached that WIP patch as a reference. Hope this helps.

Regards,

-- 
Fujii Masao
NIPPON TELEGRAPH AND TELEPHONE CORPORATION
NTT Open Source Software Center
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 31ee680..2687827 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -180,6 +180,15 @@ static const PQconninfoOption PQconninfoOptions[] = {
 	{"fallback_application_name", NULL, NULL, NULL,
 	"Fallback-Application-Name", "", 64},
 
+	{"keepalives_idle", "PGKEEPALIVESIDLE", NULL, NULL,
+	"TCP-Keepalive-Idle", "", 10}, /* strlen(INT32_MAX) == 10 */
+
+	{"keepalives_interval", "PGKEEPALIVESINTERVAL", NULL, NULL,
+	"TCP-Keepalive-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */
+
+	{"keepalives_count", "PGKEEPALIVESCOUNT", NULL, NULL,
+	"TCP-Keepalive-Count", "", 10}, /* strlen(INT32_MAX) == 10 */
+
 #ifdef USE_SSL
 
 	/*
@@ -452,6 +461,12 @@ connectOptions1(PGconn *conn, const char *conninfo)
 	conn->pgpass = tmp ? strdup(tmp) : NULL;
 	tmp = conninfo_getval(connOptions, "connect_timeout");
 	conn->connect_timeout = 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");
@@ -809,6 +824,114 @@ connectFailureMessage(PGconn *conn, int errorno)
 }
 
 
+static int
+setKeepalivesIdle(PGconn *conn)
+{
+	int	idle;
+
+	if (conn->keepalives_idle == NULL || IS_AF_UNIX(conn->laddr.addr.ss_family))
+		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;
+	}
+#else
+	if (idle != 0)
+	{
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("setsockopt(TCP_KEEPIDLE) not supported"));
+		return 0;
+	}
+#endif
+
+	return 1;
+}
+
+
+static int
+setKeepalivesInterval(PGconn *conn)
+{
+	int	interval;
+
+	if (conn->keepalives_interval == NULL || IS_AF_UNIX(conn->laddr.addr.ss_family))
+		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;
+	}
+#else
+	if (interval != 0)
+	{
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("setsockopt(TCP_KEEPINTVL) not supported"));
+		return 0;
+	}
+#endif
+
+	return 1;
+}
+
+
+static int
+setKeepalivesCount(PGconn *conn)
+{
+	int	count;
+
+	if (conn->keepalives_count == NULL || IS_AF_UNIX(conn->laddr.addr.ss_family))
+		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;
+	}
+#else
+	if (count != 0)
+	{
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("setsockopt(TCP_KEEPCNT) not supported"));
+		return 0;
+	}
+#endif
+
+	return 1;
+}
+
+
 /* ----------
  * connectDBStart -
  *		Begin the process of making a connection to the backend.
@@ -1157,8 +1280,8 @@ keep_going:						/* We will come back to here until there is
 
 					/*
 					 * Select socket options: no delay of outgoing data for
-					 * TCP sockets, nonblock mode, close-on-exec. Fail if any
-					 * of this fails.
+					 * TCP sockets, nonblock mode, close-on-exec and keepalives.
+					 * Fail if any of this fails.
 					 */
 					if (!IS_AF_UNIX(addr_cur->ai_family))
 					{
@@ -1194,6 +1317,32 @@ keep_going:						/* We will come back to here until there is
 					}
 #endif   /* F_SETFD */
 
+					if (!IS_AF_UNIX(conn->laddr.addr.ss_family))
+					{
+						int	on;
+
+						on = 1;
+						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)));
+							closesocket(conn->sock);
+							conn->sock = -1;
+							conn->addr_cur = addr_cur->ai_next;
+							continue;
+						}
+					}
+					if (!setKeepalivesIdle(conn) || !setKeepalivesInterval(conn) ||
+						!setKeepalivesCount(conn))
+					{
+						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:
@@ -2152,6 +2301,12 @@ freePGconn(PGconn *conn)
 		free(conn->pguser);
 	if (conn->pgpass)
 		free(conn->pgpass);
+	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)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 944e6ca..338dbbd 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -301,6 +301,9 @@ struct pg_conn
 	char	   *replication;	/* connect as the replication standby? */
 	char	   *pguser;			/* Postgres username and password, if any */
 	char	   *pgpass;
+	char	   *keepalives_idle;	/* time between issuing TCP keepalives */
+	char	   *keepalives_interval;	/* time between TCP keepalive retransmits */
+	char	   *keepalives_count;	/* maximum number of TCP keepalive retransmits */
 	char	   *sslmode;		/* SSL mode (require,prefer,allow,disable) */
 	char	   *sslkey;			/* client key filename */
 	char	   *sslcert;		/* client certificate filename */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to