dovecot-2.2: lib-http: Added soft_connect_timeout_msecs setting ...

dovecot at dovecot.org dovecot at dovecot.org
Sun Jun 9 02:47:06 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/07f72970bf61
changeset: 16483:07f72970bf61
user:      Timo Sirainen <tss at iki.fi>
date:      Sun Jun 09 02:46:50 2013 +0300
description:
lib-http: Added soft_connect_timeout_msecs setting to connect to multiple IPs in parallel.
Based on patch by Stephan Bosch.

diffstat:

 src/lib-http/http-client-connection.c |    4 +-
 src/lib-http/http-client-host.c       |  152 ++++++++++++++++++++++++++++++---
 src/lib-http/http-client-peer.c       |   32 +++++--
 src/lib-http/http-client-private.h    |   12 ++-
 src/lib-http/http-client.c            |    1 +
 src/lib-http/http-client.h            |    4 +
 6 files changed, 177 insertions(+), 28 deletions(-)

diffs (truncated from 368 to 300 lines):

diff -r ac78c4c88ba9 -r 07f72970bf61 src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c	Sun Jun 09 02:08:24 2013 +0300
+++ b/src/lib-http/http-client-connection.c	Sun Jun 09 02:46:50 2013 +0300
@@ -672,13 +672,13 @@
 	struct stat st;
 
 	conn->connected = TRUE;
-	conn->peer->last_connect_failed = FALSE;
-
 	if (conn->to_connect != NULL &&
 	    (conn->ssl_iostream == NULL ||
 	     ssl_iostream_is_handshaked(conn->ssl_iostream)))
 		timeout_remove(&conn->to_connect);
 
+	http_client_peer_connection_success(conn->peer);
+
 	if (conn->client->set.rawlog_dir != NULL &&
 		stat(conn->client->set.rawlog_dir, &st) == 0) {
 		iostream_rawlog_create(conn->client->set.rawlog_dir,
diff -r ac78c4c88ba9 -r 07f72970bf61 src/lib-http/http-client-host.c
--- a/src/lib-http/http-client-host.c	Sun Jun 09 02:08:24 2013 +0300
+++ b/src/lib-http/http-client-host.c	Sun Jun 09 02:46:50 2013 +0300
@@ -41,6 +41,9 @@
  * Host:port
  */
 
+static void
+http_client_host_port_connection_setup(struct http_client_host_port *hport);
+
 static struct http_client_host_port *
 http_client_host_port_find(struct http_client_host *host,
 	unsigned int port, const char *https_name)
@@ -65,6 +68,7 @@
 	hport = http_client_host_port_find(host, port, https_name);
 	if (hport == NULL) {
 		hport = array_append_space(&host->ports);
+		hport->host = host;
 		hport->port = port;
 		hport->https_name = i_strdup(https_name);
 		hport->ips_connect_idx = 0;
@@ -110,16 +114,38 @@
 	}
 }
 
-/*
- * Host
- */
+static void
+http_client_host_port_soft_connect_timeout(struct http_client_host_port *hport)
+{
+	struct http_client_host *host = hport->host;
+
+	if (hport->to_connect != NULL)
+		timeout_remove(&hport->to_connect);
+
+	if (hport->ips_connect_idx + 1 >= host->ips_count)
+		return;
+
+	/* if our our previous connection attempt takes longer than the
+	   soft_connect_timeout we start a connection attempt to the next IP in
+	   parallel */
+
+	http_client_host_debug(host, "Connection to %s:%u%s is taking a long time; "
+		"starting parallel connection attempt to next IP",
+		net_ip2addr(&host->ips[hport->ips_connect_idx]), hport->port,
+		hport->https_name == NULL ? "" :
+			t_strdup_printf(" (SSL=%s)", hport->https_name));
+
+	hport->ips_connect_idx++;
+	http_client_host_port_connection_setup(hport);
+}
 
 static void
-http_client_host_connection_setup(struct http_client_host *host,
-	struct http_client_host_port *hport)
+http_client_host_port_connection_setup(struct http_client_host_port *hport)
 {
+	struct http_client_host *host = hport->host;
 	struct http_client_peer *peer = NULL;
 	struct http_client_peer_addr addr;
+	unsigned int msecs;
 
 	addr.ip = host->ips[hport->ips_connect_idx];
 	addr.port = hport->port;
@@ -131,6 +157,107 @@
 
 	peer = http_client_peer_get(host->client, &addr);
 	http_client_peer_add_host(peer, host);
+	hport->pending_connection_count++;
+
+	/* start soft connect time-out (but only if we have another IP left) */
+	msecs = host->client->set.soft_connect_timeout_msecs;
+	if (host->ips_count - hport->ips_connect_idx > 1 && msecs > 0 &&
+	    hport->to_connect == NULL) {
+		hport->to_connect =
+			timeout_add(msecs, http_client_host_port_soft_connect_timeout, hport);
+	}
+}
+
+static void
+http_client_host_drop_pending_connections(struct http_client_host_port *hport)
+{
+	struct http_client_peer *peer;
+	struct http_client_connection *const *conns, *conn;
+	unsigned int i, count;
+
+	for (peer = hport->host->client->peers_list; peer != NULL; peer = peer->next) {
+		if (!http_client_peer_have_host(peer, hport->host))
+			continue;
+
+		conns = array_get(&peer->conns, &count);
+		for (i = count; i > 0; i--) {
+			conn = conns[i-1];
+			if (!conn->connected) {
+				i_assert(conn->refcount == 1);
+				/* avoid recreating the connection */
+				peer->last_connect_failed = TRUE;
+				http_client_connection_unref(&conn);
+			}
+		}
+	}
+}
+
+static void
+http_client_host_port_connection_success(struct http_client_host_port *hport)
+{
+	/* we achieved at least one connection the the addr->ip */
+
+	/* stop soft connect time-out */
+	if (hport->to_connect != NULL)
+		timeout_remove(&hport->to_connect);
+
+	/* drop all other attempts. note that we get here whenever a connection
+	   is successfully created, so pending_connection_count may be 0. */
+	if (hport->pending_connection_count > 1)
+		http_client_host_drop_pending_connections(hport);
+	/* since this hport is now successfully connected, we won't be
+	   getting any connection failures to it anymore. so we need
+	   to reset the pending_connection_count count here. */
+	hport->pending_connection_count = 0;
+}
+
+static bool
+http_client_host_port_connection_failure(struct http_client_host_port *hport,
+	const char *reason)
+{
+	struct http_client_host *host = hport->host;
+
+	i_assert(hport->pending_connection_count > 0);
+	if (--hport->pending_connection_count > 0)
+		return TRUE;
+
+	/* one of the connections failed. if we're not using soft timeouts,
+	   we need to try to connect to the next IP. if we are using soft
+	   timeouts, we've already tried all of the IPs by now. */
+	if (hport->to_connect != NULL)
+		timeout_remove(&hport->to_connect);
+
+	i_assert(hport->ips_connect_idx < host->ips_count);
+	if (++hport->ips_connect_idx == host->ips_count) {
+		/* all IPs failed, but retry all of them again on the
+		   next request. */
+		hport->ips_connect_idx = 0;
+		http_client_host_port_error(hport,
+			HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason);
+		return FALSE;
+	}
+
+	http_client_host_port_connection_setup(hport);
+	return TRUE;
+}
+
+/*
+ * Host
+ */
+
+void http_client_host_connection_success(struct http_client_host *host,
+	const struct http_client_peer_addr *addr)
+{
+	struct http_client_host_port *hport;
+
+	http_client_host_debug(host, "Successfully connected to %s:%u",
+		net_ip2addr(&addr->ip), addr->port);
+
+	hport = http_client_host_port_find(host, addr->port, addr->https_name);
+	if (hport == NULL)
+		return;
+
+	http_client_host_port_connection_success(hport);
 }
 
 void http_client_host_connection_failure(struct http_client_host *host,
@@ -145,18 +272,11 @@
 	if (hport == NULL)
 		return;
 
-	i_assert(hport->ips_connect_idx < host->ips_count);
-	if (++hport->ips_connect_idx == host->ips_count) {
-		/* all IPs failed, but retry all of them again on the
-		   next request. */
-		hport->ips_connect_idx = 0;
-		http_client_host_port_error(hport,
-			HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason);
+	if (!http_client_host_port_connection_failure(hport, reason)) {
+		/* failed definitively for currently queued requests */
 		if (host->client->ioloop != NULL)
 			io_loop_stop(host->client->ioloop);
-		return;
 	}
-	http_client_host_connection_setup(host, hport);
 }
 
 static void
@@ -201,7 +321,7 @@
 		unsigned int count = array_count(&hport->request_queue);
 		hport->ips_connect_idx = 0;
 		if (count > 0)
-			http_client_host_connection_setup(host, hport);
+			http_client_host_port_connection_setup(hport);
 		requests += count;
 	}
 
@@ -306,7 +426,7 @@
 	if (host->ips_count == 0)
 		return;
 	i_assert(hport->ips_connect_idx < host->ips_count);
-	http_client_host_connection_setup(host, hport);
+	http_client_host_port_connection_setup(hport);
 }
 
 struct http_client_request *
diff -r ac78c4c88ba9 -r 07f72970bf61 src/lib-http/http-client-peer.c
--- a/src/lib-http/http-client-peer.c	Sun Jun 09 02:08:24 2013 +0300
+++ b/src/lib-http/http-client-peer.c	Sun Jun 09 02:46:50 2013 +0300
@@ -76,7 +76,8 @@
 }
 
 static unsigned int
-http_client_peer_requests_pending(struct http_client_peer *peer, unsigned int *num_urgent_r)
+http_client_peer_requests_pending(struct http_client_peer *peer,
+				  unsigned int *num_urgent_r)
 {
 	struct http_client_host *const *host;
 	unsigned int num_requests = 0, num_urgent = 0, requests, urgent;
@@ -232,20 +233,22 @@
 	return peer;
 }
 
-void http_client_peer_add_host(struct http_client_peer *peer,
-			   struct http_client_host *host)
+bool http_client_peer_have_host(struct http_client_peer *peer,
+				struct http_client_host *host)
 {
 	struct http_client_host *const *host_idx;
-	bool exists = FALSE;
 
 	array_foreach(&peer->hosts, host_idx) {
-		if (*host_idx == host) {
-			exists = TRUE;
-			break;
-		}
+		if (*host_idx == host)
+			return TRUE;
 	}
+	return FALSE;
+}
 
-	if (!exists)
+void http_client_peer_add_host(struct http_client_peer *peer,
+			       struct http_client_host *host)
+{
+	if (!http_client_peer_have_host(peer, host))
 		array_append(&peer->hosts, &host, 1);
 	http_client_peer_handle_requests(peer);
 }
@@ -267,6 +270,17 @@
 	return NULL;
 }
 
+void http_client_peer_connection_success(struct http_client_peer *peer)
+{
+	struct http_client_host *const *host;
+
+	peer->last_connect_failed = FALSE;
+
+	array_foreach(&peer->hosts, host) {
+		http_client_host_connection_success(*host, &peer->addr);
+	}
+}
+
 void http_client_peer_connection_failure(struct http_client_peer *peer,
 					 const char *reason)
 {
diff -r ac78c4c88ba9 -r 07f72970bf61 src/lib-http/http-client-private.h
--- a/src/lib-http/http-client-private.h	Sun Jun 09 02:08:24 2013 +0300
+++ b/src/lib-http/http-client-private.h	Sun Jun 09 02:46:50 2013 +0300
@@ -71,15 +71,20 @@
 };
 
 struct http_client_host_port {


More information about the dovecot-cvs mailing list