dovecot-2.2: director: Fixed backend selection when multiple tag...

dovecot at dovecot.org dovecot at dovecot.org
Tue Nov 24 09:16:18 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/0e05efd14b39
changeset: 19411:0e05efd14b39
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Nov 24 11:15:47 2015 +0200
description:
director: Fixed backend selection when multiple tags were used.
The previous algorithm was causing an uneven load for backends.

This change breaks compatibility with older director servers that were using
tags because of the different selection algorithm. The new director code
refuses to run within a cluster with old directors if tags are used.

diffstat:

 src/director/director-connection.c |    9 +
 src/director/director.c            |   13 +-
 src/director/director.h            |    4 +-
 src/director/mail-host.c           |  171 ++++++++++++++++++++++++------------
 src/director/mail-host.h           |    3 +-
 5 files changed, 136 insertions(+), 64 deletions(-)

diffs (truncated from 446 to 300 lines):

diff -r 5168fdd5127e -r 0e05efd14b39 src/director/director-connection.c
--- a/src/director/director-connection.c	Mon Nov 23 19:47:08 2015 +0200
+++ b/src/director/director-connection.c	Tue Nov 24 11:15:47 2015 +0200
@@ -893,6 +893,10 @@
 		i_assert(conn->handshake_sending_hosts);
 		return TRUE;
 	}
+	if (tag[0] != '\0' && conn->minor_version < DIRECTOR_VERSION_TAGS_V2) {
+		director_cmd_error(conn, "Received a host tag from older director version with incompatible tagging support");
+		return FALSE;
+	}
 
 	host = mail_host_lookup(conn->dir->mail_hosts, &ip);
 	if (host == NULL) {
@@ -1208,6 +1212,11 @@
 				DIRECTOR_VERSION_MAJOR);
 			return -1;
 		}
+		if (conn->minor_version < DIRECTOR_VERSION_TAGS_V2 &&
+		    mail_hosts_have_tags(conn->dir->mail_hosts)) {
+			i_error("director(%s): Director version supports incompatible tags", conn->name);
+			return FALSE;
+		}
 		conn->version_received = TRUE;
 		if (conn->done_pending) {
 			if (director_connection_send_done(conn) < 0)
diff -r 5168fdd5127e -r 0e05efd14b39 src/director/director.c
--- a/src/director/director.c	Mon Nov 23 19:47:08 2015 +0200
+++ b/src/director/director.c	Tue Nov 24 11:15:47 2015 +0200
@@ -536,13 +536,18 @@
 		    net_ip2addr(&orig_src->ip), orig_src->port,
 		    orig_src->last_seq,
 		    net_ip2addr(&host->ip), host->vhost_count);
-	if (dir->ring_min_version >= DIRECTOR_VERSION_TAGS) {
+	if (dir->ring_min_version >= DIRECTOR_VERSION_TAGS_V2) {
 		str_append_c(str, '\t');
 		str_append_tabescaped(str, host_tag);
 	} else if (host_tag[0] != '\0' &&
-		   dir->ring_min_version < DIRECTOR_VERSION_TAGS) {
-		i_error("Ring has directors that don't support tags - removing host %s with tag '%s'",
-			net_ip2addr(&host->ip), host_tag);
+		   dir->ring_min_version < DIRECTOR_VERSION_TAGS_V2) {
+		if (dir->ring_min_version < DIRECTOR_VERSION_TAGS) {
+			i_error("Ring has directors that don't support tags - removing host %s with tag '%s'",
+				net_ip2addr(&host->ip), host_tag);
+		} else {
+			i_error("Ring has directors that support mixed versions of tags - removing host %s with tag '%s'",
+				net_ip2addr(&host->ip), host_tag);
+		}
 		director_remove_host(dir, NULL, NULL, host);
 		return;
 	}
diff -r 5168fdd5127e -r 0e05efd14b39 src/director/director.h
--- a/src/director/director.h	Mon Nov 23 19:47:08 2015 +0200
+++ b/src/director/director.h	Tue Nov 24 11:15:47 2015 +0200
@@ -6,7 +6,7 @@
 
 #define DIRECTOR_VERSION_NAME "director"
 #define DIRECTOR_VERSION_MAJOR 1
-#define DIRECTOR_VERSION_MINOR 6
+#define DIRECTOR_VERSION_MINOR 7
 
 /* weak users supported in protocol */
 #define DIRECTOR_VERSION_WEAK_USERS 1
@@ -22,6 +22,8 @@
 #define DIRECTOR_VERSION_TAGS 5
 /* up/down state is tracked */
 #define DIRECTOR_VERSION_UPDOWN 6
+/* user tag version 2 supported */
+#define DIRECTOR_VERSION_TAGS_V2 7
 
 /* Minimum time between even attempting to communicate with a director that
    failed due to a protocol error. */
diff -r 5168fdd5127e -r 0e05efd14b39 src/director/mail-host.c
--- a/src/director/mail-host.c	Mon Nov 23 19:47:08 2015 +0200
+++ b/src/director/mail-host.c	Tue Nov 24 11:15:47 2015 +0200
@@ -14,12 +14,19 @@
 	struct mail_host *host;
 };
 
+struct mail_tag {
+	/* "" = no tag */
+	char *name;
+	ARRAY(struct mail_vhost) vhosts;
+};
+
 struct mail_host_list {
+	ARRAY(struct mail_tag *) tags;
 	ARRAY_TYPE(mail_host) hosts;
-	ARRAY(struct mail_vhost) vhosts;
 	unsigned int hosts_hash;
+	bool consistent_hashing;
 	bool vhosts_unsorted;
-	bool consistent_hashing;
+	bool have_vhosts;
 };
 
 static int
@@ -51,7 +58,7 @@
 		return 0;
 }
 
-static void mail_vhost_add(struct mail_host_list *list, struct mail_host *host)
+static void mail_vhost_add(struct mail_tag *tag, struct mail_host *host)
 {
 	struct mail_vhost *vhost;
 	struct md5_context md5_ctx, md5_ctx2;
@@ -74,56 +81,65 @@
 		md5_update(&md5_ctx2, num_str, strlen(num_str));
 		md5_final(&md5_ctx2, md5);
 
-		vhost = array_append_space(&list->vhosts);
+		vhost = array_append_space(&tag->vhosts);
 		vhost->host = host;
 		for (j = 0; j < sizeof(vhost->hash); j++)
 			vhost->hash = (vhost->hash << CHAR_BIT) | md5[j];
 	}
 }
 
-static void mail_hosts_sort_ring(struct mail_host_list *list)
+static void
+mail_tag_vhosts_sort_ring(struct mail_host_list *list, struct mail_tag *tag)
 {
 	struct mail_host *const *hostp;
 
 	/* rebuild vhosts */
-	array_clear(&list->vhosts);
+	array_clear(&tag->vhosts);
 	array_foreach(&list->hosts, hostp)
-		mail_vhost_add(list, *hostp);
-	array_sort(&list->vhosts, mail_vhost_cmp);
-	list->vhosts_unsorted = FALSE;
+		mail_vhost_add(tag, *hostp);
+	array_sort(&tag->vhosts, mail_vhost_cmp);
 }
 
-static void mail_hosts_sort_direct(struct mail_host_list *list)
+static void
+mail_tag_vhosts_sort_direct(struct mail_host_list *list, struct mail_tag *tag)
 {
 	struct mail_vhost *vhost;
 	struct mail_host *const *hostp;
 	unsigned int i;
 
 	/* rebuild vhosts */
-	array_clear(&list->vhosts);
+	array_clear(&tag->vhosts);
 	array_foreach(&list->hosts, hostp) {
 		if ((*hostp)->down)
 			continue;
 		for (i = 0; i < (*hostp)->vhost_count; i++) {
-			vhost = array_append_space(&list->vhosts);
+			vhost = array_append_space(&tag->vhosts);
 			vhost->host = *hostp;
 		}
 	}
-	list->vhosts_unsorted = FALSE;
 }
 
-static void mail_hosts_sort(struct mail_host_list *list)
+static void
+mail_hosts_sort(struct mail_host_list *list)
 {
 	struct mail_host *const *hostp;
+	struct mail_tag *const *tagp;
 	uint32_t num;
 
 	array_sort(&list->hosts, mail_host_cmp);
 
-	if (list->consistent_hashing)
-		mail_hosts_sort_ring(list);
-	else
-		mail_hosts_sort_direct(list);
+	list->have_vhosts = FALSE;
+	array_foreach(&list->tags, tagp) {
+		if (list->consistent_hashing)
+			mail_tag_vhosts_sort_ring(list, *tagp);
+		else
+			mail_tag_vhosts_sort_direct(list, *tagp);
+		if (array_count(&(*tagp)->vhosts) > 0)
+			list->have_vhosts = TRUE;
+	}
+	list->vhosts_unsorted = FALSE;
 
+	/* recalculate the hosts_hash */
 	list->hosts_hash = 0;
 	array_foreach(&list->hosts, hostp) {
 		num = ((*hostp)->down ? 1 : 0) ^ (*hostp)->vhost_count;
@@ -135,6 +151,40 @@
 	}
 }
 
+static struct mail_tag *
+mail_tag_find(struct mail_host_list *list, const char *tag_name)
+{
+	struct mail_tag *const *tagp;
+
+	array_foreach(&list->tags, tagp) {
+		if (strcmp((*tagp)->name, tag_name) == 0)
+			return *tagp;
+	}
+	return NULL;
+}
+
+static struct mail_tag *
+mail_tag_get(struct mail_host_list *list, const char *tag_name)
+{
+	struct mail_tag *tag;
+
+	tag = mail_tag_find(list, tag_name);
+	if (tag == NULL) {
+		tag = i_new(struct mail_tag, 1);
+		tag->name = i_strdup(tag_name);
+		i_array_init(&tag->vhosts, 16*VHOST_MULTIPLIER);
+		array_append(&list->tags, &tag, 1);
+	}
+	return tag;
+}
+
+static void mail_tag_free(struct mail_tag *tag)
+{
+	array_free(&tag->vhosts);
+	i_free(tag->name);
+	i_free(tag);
+}
+
 struct mail_host *
 mail_host_add_ip(struct mail_host_list *list, const struct ip_addr *ip,
 		 const char *tag_name)
@@ -147,7 +197,7 @@
 	host->list = list;
 	host->vhost_count = VHOST_MULTIPLIER;
 	host->ip = *ip;
-	host->tag = i_strdup(tag_name);
+	host->tag = mail_tag_get(list, tag_name);
 	array_append(&list->hosts, &host, 1);
 
 	list->vhosts_unsorted = TRUE;
@@ -302,15 +352,15 @@
 
 const char *mail_host_get_tag(struct mail_host *host)
 {
-	return host->tag;
+	return host->tag->name;
 }
 
 void mail_host_set_tag(struct mail_host *host, const char *tag_name)
 {
 	i_assert(tag_name != NULL);
 
-	i_free(host->tag);
-	host->tag = i_strdup(tag_name);
+	host->tag = mail_tag_get(host->list, tag_name);
+	host->list->vhosts_unsorted = TRUE;
 }
 
 void mail_host_set_down(struct mail_host *host, bool down, time_t timestamp)
@@ -330,7 +380,6 @@
 
 static void mail_host_free(struct mail_host *host)
 {
-	i_free(host->tag);
 	i_free(host->hostname);
 	i_free(host);
 }
@@ -344,11 +393,10 @@
 	hosts = array_get(&list->hosts, &count);
 	for (i = 0; i < count; i++) {
 		if (hosts[i] == host) {
-			array_delete(&list->hosts, i, 1);
+			array_delete(&host->list->hosts, i, 1);
 			break;
 		}
 	}
-
 	mail_host_free(host);
 	list->vhosts_unsorted = TRUE;
 }
@@ -369,15 +417,13 @@
 }
 
 static struct mail_host *
-mail_host_get_by_hash_ring(struct mail_host_list *list, unsigned int hash,
-			   const char *tag_name)
+mail_host_get_by_hash_ring(struct mail_tag *tag, unsigned int hash)
 {
-	struct mail_host *host;
 	const struct mail_vhost *vhosts;
-	unsigned int i, count, idx;
+	unsigned int count, idx;
 
-	vhosts = array_get(&list->vhosts, &count);
-	array_bsearch_insert_pos(&list->vhosts, &hash,
+	vhosts = array_get(&tag->vhosts, &count);
+	array_bsearch_insert_pos(&tag->vhosts, &hash,
 				 mail_vhost_hash_cmp, &idx);
 	i_assert(idx <= count);
 	if (idx == count) {
@@ -385,46 +431,38 @@
 			return NULL;


More information about the dovecot-cvs mailing list