dovecot-2.2: director: Added director_consistent_hashing setting.

dovecot at dovecot.org dovecot at dovecot.org
Wed Nov 12 01:46:25 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/b9df3d654710
changeset: 18065:b9df3d654710
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Nov 12 03:29:04 2014 +0200
description:
director: Added director_consistent_hashing setting.
This should have been the default since the beginning. I didn't thik it was
worth the trouble originally because in theory it shouldn't matter much if
users jump between multiple backends. But this makes caching behavior worse,
especially for systems which are using local cache files.

diffstat:

 src/director/director-connection.c |   55 ++++++++++++++-
 src/director/director-settings.c   |    1 +
 src/director/director-settings.h   |    1 +
 src/director/director.c            |    2 +-
 src/director/director.h            |    4 +-
 src/director/doveadm-connection.c  |    2 +-
 src/director/mail-host.c           |  135 +++++++++++++++++++++++++++++++++---
 src/director/mail-host.h           |    2 +-
 8 files changed, 184 insertions(+), 18 deletions(-)

diffs (truncated from 396 to 300 lines):

diff -r 389f084b3d2a -r b9df3d654710 src/director/director-connection.c
--- a/src/director/director-connection.c	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/director-connection.c	Wed Nov 12 03:29:04 2014 +0200
@@ -90,6 +90,8 @@
 #define CMD_IS_USER_HANDHAKE(args) \
 	(str_array_length(args) > 2)
 
+#define DIRECTOR_OPT_CONSISTENT_HASHING "consistent-hashing"
+
 struct director_connection {
 	struct director *dir;
 	char *name;
@@ -125,6 +127,7 @@
 	unsigned int wrong_host:1;
 	unsigned int verifying_left:1;
 	unsigned int users_unsorted:1;
+	unsigned int done_pending:1;
 };
 
 static void director_connection_disconnected(struct director_connection **conn);
@@ -132,6 +135,7 @@
 					  const char *reason);
 static void
 director_connection_log_disconnect(struct director_connection *conn, int err);
+static int director_connection_send_done(struct director_connection *conn);
 
 static void ATTR_FORMAT(2, 3)
 director_cmd_error(struct director_connection *conn, const char *fmt, ...)
@@ -1070,6 +1074,25 @@
 }
 
 static int
+director_handshake_cmd_options(struct director_connection *conn,
+			       const char *const *args)
+{
+	bool consistent_hashing = FALSE;
+	unsigned int i;
+
+	for (i = 0; args[i] != NULL; i++) {
+		if (strcmp(args[i], DIRECTOR_OPT_CONSISTENT_HASHING) == 0)
+			consistent_hashing = TRUE;
+	}
+	if (consistent_hashing != conn->dir->set->director_consistent_hashing) {
+		i_error("director(%s): director_consistent_hashing settings differ between directors",
+			conn->name);
+		return -1;
+	}
+	return 1;
+}
+
+static int
 director_connection_handle_handshake(struct director_connection *conn,
 				     const char *cmd, const char *const *args)
 {
@@ -1088,6 +1111,10 @@
 		}
 		conn->minor_version = atoi(args[2]);
 		conn->version_received = TRUE;
+		if (conn->done_pending) {
+			if (director_connection_send_done(conn) < 0)
+				return -1;
+		}
 		return 1;
 	}
 	if (!conn->version_received) {
@@ -1114,6 +1141,8 @@
 		director_cmd_error(conn, "Unexpected command during host list");
 		return -1;
 	}
+	if (strcmp(cmd, "OPTIONS") == 0)
+		return director_handshake_cmd_options(conn, args);
 	if (strcmp(cmd, "HOST-HAND-START") == 0) {
 		if (!conn->in) {
 			director_cmd_error(conn,
@@ -1518,6 +1547,25 @@
 	str_printfa(str, "HOST-HAND-END\t%u\n", conn->dir->ring_handshaked);
 }
 
+static int director_connection_send_done(struct director_connection *conn)
+{
+	i_assert(conn->version_received);
+
+	if (!conn->dir->set->director_consistent_hashing)
+		;
+	else if (conn->minor_version >= DIRECTOR_VERSION_OPTIONS) {
+		director_connection_send(conn,
+			"OPTIONS\t"DIRECTOR_OPT_CONSISTENT_HASHING"\n");
+	} else {
+		i_error("director(%s): Director version is too old for supporting director_consistent_hashing=yes",
+			conn->name);
+		return -1;
+	}
+	director_connection_send(conn, "DONE\n");
+	conn->done_pending = FALSE;
+	return 0;
+}
+
 static int director_connection_send_users(struct director_connection *conn)
 {
 	struct user *user;
@@ -1546,7 +1594,12 @@
 		}
 	}
 	user_directory_iter_deinit(&conn->user_iter);
-	director_connection_send(conn, "DONE\n");
+	if (!conn->version_received)
+		conn->done_pending = TRUE;
+	else {
+		if (director_connection_send_done(conn) < 0)
+			return -1;
+	}
 
 	if (conn->users_unsorted && conn->handshake_received) {
 		/* we received remote's list of users before sending ours */
diff -r 389f084b3d2a -r b9df3d654710 src/director/director-settings.c
--- a/src/director/director-settings.c	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/director-settings.c	Wed Nov 12 03:29:04 2014 +0200
@@ -73,6 +73,7 @@
 	DEF(SET_STR, director_username_hash),
 	DEF(SET_TIME, director_user_expire),
 	DEF(SET_UINT, director_doveadm_port),
+	DEF(SET_BOOL, director_consistent_hashing),
 
 	SETTING_DEFINE_LIST_END
 };
diff -r 389f084b3d2a -r b9df3d654710 src/director/director-settings.h
--- a/src/director/director-settings.h	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/director-settings.h	Wed Nov 12 03:29:04 2014 +0200
@@ -9,6 +9,7 @@
 	const char *director_username_hash;
 	unsigned int director_user_expire;
 	unsigned int director_doveadm_port;
+	bool director_consistent_hashing;
 };
 
 extern const struct setting_parser_info director_setting_parser_info;
diff -r 389f084b3d2a -r b9df3d654710 src/director/director.c
--- a/src/director/director.c	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/director.c	Wed Nov 12 03:29:04 2014 +0200
@@ -909,7 +909,7 @@
 	i_array_init(&dir->connections, 8);
 	dir->users = user_directory_init(set->director_user_expire,
 					 set->director_username_hash);
-	dir->mail_hosts = mail_hosts_init();
+	dir->mail_hosts = mail_hosts_init(set->director_consistent_hashing);
 
 	dir->ipc_proxy = ipc_client_init(DIRECTOR_IPC_PROXY_PATH);
 	dir->ring_min_version = DIRECTOR_VERSION_MINOR;
diff -r 389f084b3d2a -r b9df3d654710 src/director/director.h
--- a/src/director/director.h	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/director.h	Wed Nov 12 03:29:04 2014 +0200
@@ -6,7 +6,7 @@
 
 #define DIRECTOR_VERSION_NAME "director"
 #define DIRECTOR_VERSION_MAJOR 1
-#define DIRECTOR_VERSION_MINOR 4
+#define DIRECTOR_VERSION_MINOR 5
 
 /* weak users supported in protocol */
 #define DIRECTOR_VERSION_WEAK_USERS 1
@@ -16,6 +16,8 @@
 #define DIRECTOR_VERSION_QUIT 3
 /* user-kick supported */
 #define DIRECTOR_VERSION_USER_KICK 4
+/* options supported in handshake */
+#define DIRECTOR_VERSION_OPTIONS 5
 
 /* Minimum time between even attempting to communicate with a director that
    failed due to a protocol error. */
diff -r 389f084b3d2a -r b9df3d654710 src/director/doveadm-connection.c
--- a/src/director/doveadm-connection.c	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/doveadm-connection.c	Wed Nov 12 03:29:04 2014 +0200
@@ -62,7 +62,7 @@
 	string_t *str = t_str_new(1024);
 	int ret;
 
-	orig_hosts_list = mail_hosts_init();
+	orig_hosts_list = mail_hosts_init(conn->dir->set->director_consistent_hashing);
 	(void)mail_hosts_parse_and_add(orig_hosts_list,
 				       conn->dir->set->director_mail_servers);
 
diff -r 389f084b3d2a -r b9df3d654710 src/director/mail-host.c
--- a/src/director/mail-host.c	Wed Nov 12 02:04:14 2014 +0200
+++ b/src/director/mail-host.c	Wed Nov 12 03:29:04 2014 +0200
@@ -2,14 +2,22 @@
 
 #include "lib.h"
 #include "array.h"
+#include "bsearch-insert-pos.h"
+#include "md5.h"
 #include "mail-host.h"
 
 #define VHOST_MULTIPLIER 100
 
+struct mail_vhost {
+	unsigned int hash;
+	struct mail_host *host;
+};
+
 struct mail_host_list {
 	ARRAY_TYPE(mail_host) hosts;
-	ARRAY(struct mail_host *) vhosts;
+	ARRAY(struct mail_vhost) vhosts;
 	bool hosts_unsorted;
+	bool consistent_hashing;
 };
 
 static int
@@ -18,8 +26,71 @@
 	return net_ip_cmp(&(*h1)->ip, &(*h2)->ip);
 }
 
-static void mail_hosts_sort(struct mail_host_list *list)
+static int
+mail_vhost_cmp(const struct mail_vhost *h1, const struct mail_vhost *h2)
 {
+	if (h1->hash < h2->hash)
+		return -1;
+	else if (h1->hash > h2->hash)
+		return 1;
+	/* hash collision. not ideal, but we'll need to keep the order
+	   consistent across directors so compare the IPs next. */
+	return net_ip_cmp(&h1->host->ip, &h2->host->ip);
+}
+
+static int
+mail_vhost_hash_cmp(const unsigned int *hash, const struct mail_vhost *vhost)
+{
+	if (vhost->hash < *hash)
+		return 1;
+	else if (vhost->hash > *hash)
+		return -1;
+	else
+		return 0;
+}
+
+static void mail_vhost_add(struct mail_host_list *list, struct mail_host *host)
+{
+	struct mail_vhost *vhost;
+	struct md5_context md5_ctx, md5_ctx2;
+	unsigned char md5[MD5_RESULTLEN];
+	const char *ip_str;
+	char num_str[MAX_INT_STRLEN];
+	unsigned int i, j;
+
+	ip_str = net_ip2addr(&host->ip);
+
+	md5_init(&md5_ctx);
+	md5_update(&md5_ctx, ip_str, strlen(ip_str));
+
+	for (i = 0; i < host->vhost_count; i++) {
+		md5_ctx2 = md5_ctx;
+		i_snprintf(num_str, sizeof(num_str), "-%u", i);
+		md5_update(&md5_ctx2, num_str, strlen(num_str));
+		md5_final(&md5_ctx2, md5);
+
+		vhost = array_append_space(&list->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)
+{
+	struct mail_host *const *hostp;
+
+	/* rebuild vhosts */
+	array_clear(&list->vhosts);
+	array_foreach(&list->hosts, hostp)
+		mail_vhost_add(list, *hostp);
+	array_sort(&list->vhosts, mail_vhost_cmp);
+	list->hosts_unsorted = FALSE;
+}
+
+static void mail_hosts_sort_direct(struct mail_host_list *list)
+{
+	struct mail_vhost *vhost;
 	struct mail_host *const *hostp;
 	unsigned int i;
 
@@ -28,12 +99,22 @@
 	/* rebuild vhosts */
 	array_clear(&list->vhosts);
 	array_foreach(&list->hosts, hostp) {
-		for (i = 0; i < (*hostp)->vhost_count; i++)
-			array_append(&list->vhosts, hostp, 1);
+		for (i = 0; i < (*hostp)->vhost_count; i++) {
+			vhost = array_append_space(&list->vhosts);
+			vhost->host = *hostp;
+		}
 	}
 	list->hosts_unsorted = FALSE;
 }
 
+static void mail_hosts_sort(struct mail_host_list *list)
+{
+	if (list->consistent_hashing)
+		mail_hosts_sort_ring(list);
+	else


More information about the dovecot-cvs mailing list