]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
director: A lot of fixes.
authorTimo Sirainen <tss@iki.fi>
Fri, 18 Jun 2010 18:21:50 +0000 (19:21 +0100)
committerTimo Sirainen <tss@iki.fi>
Fri, 18 Jun 2010 18:21:50 +0000 (19:21 +0100)
--HG--
branch : HEAD

src/director/director-connection.c
src/director/director-connection.h
src/director/director-request.c
src/director/director.c
src/director/director.h
src/director/doveadm-connection.c
src/director/mail-host.c
src/director/main.c

index 4fff94e6848a773440bb63f2328559a22a33551b..796c22eae9d4fcedbb8713fbdf0c99edcc23c425 100644 (file)
@@ -49,6 +49,7 @@ struct director_connection {
        unsigned int me_received:1;
        unsigned int handshake_received:1;
        unsigned int ignore_host_events:1;
+       unsigned int handshake_sending_hosts:1;
 };
 
 static void director_connection_ping(struct director_connection *conn);
@@ -176,12 +177,17 @@ director_user_refresh(struct director *dir, unsigned int username_hash,
                        net_ip2addr(&user->host->ip),
                        net_ip2addr(&host->ip));
 
-               /* change the host anyway. we'll also need to remove the user
-                  from the old host's user_count, because we can't keep track
-                  of the user for more than one host */
-               user->host->user_count--;
-               user->host = host;
-               user->host->user_count++;
+               /* we want all the directors to redirect the user to same
+                  server, but we don't want two directors fighting over which
+                  server it belongs to, so always use the lower IP address */
+               if (net_ip_cmp(&user->host->ip, &host->ip) > 0) {
+                       /* change the host. we'll also need to remove the user
+                          from the old host's user_count, because we can't
+                          keep track of the user for more than one host */
+                       user->host->user_count--;
+                       user->host = host;
+                       user->host->user_count++;
+               }
                ret = TRUE;
        }
        *user_r = user;
@@ -265,6 +271,7 @@ director_cmd_host_hand_start(struct director_connection *conn,
                /* ignore whatever remote sends */
                conn->ignore_host_events = TRUE;
        }
+       conn->handshake_sending_hosts = TRUE;
        return TRUE;
 }
 
@@ -285,6 +292,7 @@ director_cmd_host(struct director_connection *conn, const char *const *args)
        if (conn->ignore_host_events) {
                /* remote is sending hosts in a handshake, but it doesn't have
                   a completed ring and we do. */
+               i_assert(conn->handshake_sending_hosts);
                return TRUE;
        }
 
@@ -297,9 +305,8 @@ director_cmd_host(struct director_connection *conn, const char *const *args)
        }
 
        if (update) {
-               /* FIXME: 1) shouldn't be unconditional, 2) if we're not
-                  handshaking, we should do SYNC before making it visible */
-               host->vhost_count = vhost_count;
+               mail_host_set_vhost_count(conn->dir->mail_hosts,
+                                         host, vhost_count);
                director_update_host(conn->dir, conn->host, host);
        }
        return TRUE;
@@ -365,7 +372,8 @@ static void director_handshake_cmd_done(struct director_connection *conn)
                }
        }
 
-       if (dir->left != NULL && dir->right != NULL) {
+       if (dir->left != NULL && dir->right != NULL &&
+           dir->left->handshake_received && dir->right->handshake_received) {
                /* we're connected to both directors. see if the ring is
                   finished by sending a SYNC. if we get it back, it's done. */
                dir->sync_seq = ++dir->self_host->last_seq;
@@ -424,20 +432,28 @@ director_connection_handle_handshake(struct director_connection *conn,
        /* only incoming connections get DIRECTOR and HOST lists */
        if (conn->in && strcmp(cmd, "DIRECTOR") == 0 && conn->me_received)
                return director_cmd_director(conn, args);
-       if (conn->in && strcmp(cmd, "HOST") == 0 && conn->me_received)
+
+       if (strcmp(cmd, "HOST") == 0) {
+               /* allow hosts from all connections always,
+                  this could be an host update */
                return director_cmd_host(conn, args);
-       if (strcmp(cmd, "HOST-HAND-START") == 0)
-               return director_cmd_host_hand_start(conn, args);
-       if (strcmp(cmd, "HOST-HAND-END") == 0) {
-               conn->ignore_host_events = TRUE;
+       }
+       if (conn->handshake_sending_hosts &&
+           strcmp(cmd, "HOST-HAND-END") == 0) {
+               conn->ignore_host_events = FALSE;
+               conn->handshake_sending_hosts = FALSE;
                return TRUE;
        }
+       if (conn->in && strcmp(cmd, "HOST-HAND-START") == 0 &&
+           conn->me_received)
+               return director_cmd_host_hand_start(conn, args);
 
        /* only incoming connections get a USER list */
        if (conn->in && strcmp(cmd, "USER") == 0 && conn->me_received)
                return director_handshake_cmd_user(conn, args);
        /* both get DONE */
-       if (strcmp(cmd, "DONE") == 0 && !conn->handshake_received) {
+       if (strcmp(cmd, "DONE") == 0 && !conn->handshake_received &&
+           !conn->handshake_sending_hosts) {
                director_handshake_cmd_done(conn);
                return TRUE;
        }
@@ -476,6 +492,7 @@ director_cmd_user(struct director_connection *conn, const char *const *args)
 static bool director_connection_sync(struct director_connection *conn,
                                     const char *const *args, const char *line)
 {
+       struct director *dir = conn->dir;
        struct director_host *host;
        struct ip_addr ip;
        unsigned int port, seq;
@@ -489,26 +506,39 @@ static bool director_connection_sync(struct director_connection *conn,
 
        /* find the originating director. if we don't see it, it was already
           removed and we can ignore this sync. */
-       host = director_host_lookup(conn->dir, &ip, port);
+       host = director_host_lookup(dir, &ip, port);
        if (host == NULL)
                return TRUE;
 
        if (host->self) {
-               if (conn->dir->sync_seq != seq) {
+               if (dir->sync_seq != seq) {
                        /* stale SYNC event */
                        return TRUE;
                }
-               if (conn->dir->ring_handshaked)
+               if (!dir->ring_handshaked) {
+                       /* the ring is handshaked */
+                       director_set_ring_handshaked(dir);
+                       return TRUE;
+               }
+
+               if (dir->ring_synced) {
+                       i_error("Received SYNC from %s (seq=%u) "
+                               "while already synced", conn->name, seq);
                        return TRUE;
+               }
 
-               /* the ring is handshaked */
-               director_set_ring_handshaked(conn->dir);
+               if (dir->debug) {
+                       i_debug("Ring is synced (%s sent seq=%u)",
+                               conn->name, seq);
+               }
+               dir->ring_synced = TRUE;
+               director_set_state_changed(dir);
                return TRUE;
        }
 
        /* forward it to the connection on right */
-       if (conn->dir->right != NULL) {
-               director_connection_send(conn->dir->right,
+       if (dir->right != NULL) {
+               director_connection_send(dir->right,
                                         t_strconcat(line, "\n", NULL));
        }
        return TRUE;
@@ -884,3 +914,8 @@ static void director_connection_ping(struct director_connection *conn)
                                    director_connection_ping_timeout, conn);
        director_connection_send(conn, "PING\n");
 }
+
+const char *director_connection_get_name(struct director_connection *conn)
+{
+       return conn->name;
+}
index c56bbabc3bc75e00e921bf45bc8cd99a5f116a8f..e2272a43f666e35951449847fd31370dd87c63fc 100644 (file)
@@ -17,4 +17,6 @@ void director_connection_send_except(struct director_connection *conn,
                                     struct director_host *skip_host,
                                     const char *data);
 
+const char *director_connection_get_name(struct director_connection *conn);
+
 #endif
index 1ff32c7ddda821e34c6784a7167426951617f3ac..716232a0a84466f248fee6e55c4c1d9ada4382ed 100644 (file)
@@ -75,7 +75,7 @@ bool director_request_continue(struct director_request *request)
        if (!dir->ring_handshaked) {
                /* delay requests until ring handshaking is complete */
                if (!dir->ring_handshake_warning_sent) {
-                       i_warning("Delaying connections until all "
+                       i_warning("Delaying requests until all "
                                  "directors have connected");
                        dir->ring_handshake_warning_sent = TRUE;
                }
@@ -86,8 +86,10 @@ bool director_request_continue(struct director_request *request)
        if (user != NULL)
                user_directory_refresh(dir->users, user);
        else {
-               if (array_count(&dir->desynced_host_changes) != 0) {
+               if (!dir->ring_synced) {
                        /* delay adding new users until ring is again synced */
+                       if (dir->debug)
+                               i_debug("Delaying request until ring is synced");
                        return FALSE;
                }
                host = mail_host_get_by_hash(dir->mail_hosts,
index 6b6e7ac22b5b491a0cfd0864c71c14354fe434e6..a4e54dd1fa6c0eeeed98c0a1b7169b8dfb45f5c4 100644 (file)
@@ -137,9 +137,26 @@ void director_set_ring_handshaked(struct director *dir)
                i_debug("Director ring handshaked");
 
        dir->ring_handshaked = TRUE;
+       dir->ring_synced = TRUE;
        director_set_state_changed(dir);
 }
 
+static void director_sync(struct director *dir)
+{
+       /* we're synced again, once we receive this SYNC back */
+       dir->sync_seq++;
+       dir->ring_synced = FALSE;
+
+       if (dir->debug) {
+               i_debug("Ring is desynced (seq=%u, sending SYNC to %s)",
+                       dir->sync_seq, director_connection_get_name(dir->right));
+       }
+
+       director_connection_send(dir->right, t_strdup_printf(
+               "SYNC\t%s\t%u\t%u\n", net_ip2addr(&dir->self_ip),
+               dir->self_port, dir->sync_seq));
+}
+
 void director_update_host(struct director *dir, struct director_host *src,
                          struct mail_host *host)
 {
@@ -147,6 +164,7 @@ void director_update_host(struct director *dir, struct director_host *src,
 
        director_update_send(dir, src, t_strdup_printf(
                "HOST\t%s\t%u\n", net_ip2addr(&host->ip), host->vhost_count));
+       director_sync(dir);
 }
 
 void director_remove_host(struct director *dir, struct director_host *src,
@@ -156,6 +174,7 @@ void director_remove_host(struct director *dir, struct director_host *src,
                "HOST-REMOVE\t%s\n", net_ip2addr(&host->ip)));
        user_directory_remove_host(dir->users, host);
        mail_host_remove(dir->mail_hosts, host);
+       director_sync(dir);
 }
 
 void director_flush_host(struct director *dir, struct director_host *src,
@@ -164,6 +183,7 @@ void director_flush_host(struct director *dir, struct director_host *src,
        director_update_send(dir, src, t_strdup_printf(
                "HOST-FLUSH\t%s\n", net_ip2addr(&host->ip)));
        user_directory_remove_host(dir->users, host);
+       director_sync(dir);
 }
 
 void director_update_user(struct director *dir, struct director_host *src,
@@ -204,7 +224,6 @@ director_init(const struct director_settings *set,
        dir->state_change_callback = callback;
        i_array_init(&dir->dir_hosts, 16);
        i_array_init(&dir->pending_requests, 16);
-       i_array_init(&dir->desynced_host_changes, 16);
        dir->users = user_directory_init(set->director_user_expire);
        dir->mail_hosts = mail_hosts_init();
        return dir;
@@ -229,7 +248,6 @@ void director_deinit(struct director **_dir)
                timeout_remove(&dir->to_request);
        array_foreach(&dir->dir_hosts, hostp)
                director_host_free(*hostp);
-       array_free(&dir->desynced_host_changes);
        array_free(&dir->pending_requests);
        array_free(&dir->dir_hosts);
        i_free(dir);
index 9fc075750bb6af451baa5b3b9e67322fa85a17a7..1782dc2473b3129e7a84d94dd4af68f9f6591094 100644 (file)
@@ -49,18 +49,13 @@ struct director {
        /* director hosts are sorted by IP (and port) */
        ARRAY_DEFINE(dir_hosts, struct director_host *);
 
-       /* this array contains host changes done by directors.
-          while it's non-empty, new user mappings can't be added, because
-          different directors may see different hosts. SYNC events remove
-          these changes. */
-       ARRAY_DEFINE(desynced_host_changes, struct director_host_change);
-
        unsigned int sync_seq;
 
        /* director ring handshaking is complete.
           director can start serving clients. */
        unsigned int ring_handshaked:1;
        unsigned int ring_handshake_warning_sent:1;
+       unsigned int ring_synced:1;
        unsigned int debug:1;
 };
 
index 05fac1f38b50e8515022959e0b3cb143c311a556..6cd3004b4cb7bb6c5cd648e3c1e589e537c1a917 100644 (file)
@@ -79,7 +79,7 @@ doveadm_cmd_host_set(struct doveadm_connection *conn, const char *line)
        if (args[0] == NULL ||
            net_addr2ip(args[0], &ip) < 0 ||
            (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0)) {
-               i_error("doveadm sent invalid HOST-SET parameters");
+               i_error("doveadm sent invalid HOST-SET parameters: %s", line);
                return FALSE;
        }
        if (vhost_count > MAX_VALID_VHOST_COUNT && vhost_count != -1U) {
@@ -160,7 +160,8 @@ doveadm_cmd_user_lookup(struct doveadm_connection *conn, const char *line)
        unsigned int username_hash;
        string_t *str = t_str_new(256);
 
-       username_hash = user_directory_get_username_hash(line);
+       if (str_to_uint(line, &username_hash) < 0)
+               username_hash = user_directory_get_username_hash(line);
 
        /* get user's current host */
        user = user_directory_lookup(conn->dir->users, username_hash);
index be2b662825e4a0115a4d386aafb848a691463716..908e96dde4dab88133ecb9b6ba26492e31bf6839 100644 (file)
@@ -164,7 +164,7 @@ void mail_host_set_vhost_count(struct mail_host_list *list,
                               struct mail_host *host, unsigned int vhost_count)
 {
        host->vhost_count = vhost_count;
-       mail_hosts_sort(list);
+       list->hosts_unsorted = TRUE;
 }
 
 void mail_host_remove(struct mail_host_list *list, struct mail_host *host)
@@ -181,7 +181,7 @@ void mail_host_remove(struct mail_host_list *list, struct mail_host *host)
        }
 
        i_free(host);
-       mail_hosts_sort(list);
+       list->hosts_unsorted = TRUE;
 }
 
 struct mail_host *
index f3b72e87d345cf3b3e3c7e0e119dda58740160fe..71d8a613b5cf2aee0abae8382764b58ebad7ac2e 100644 (file)
@@ -107,8 +107,7 @@ static void director_state_changed(struct director *dir)
        struct director_request *const *requestp;
        bool ret;
 
-       if (!dir->ring_handshaked ||
-           array_count(&dir->desynced_host_changes) != 0 ||
+       if (!dir->ring_synced ||
            mail_host_get_by_hash(dir->mail_hosts, 0) == NULL)
                return;