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