Mercurial > dovecot > original-hg > dovecot-2.1
changeset 14988:c9d7586d01ae
director: Avoid infinite SYNC loops if the originating director goes away for a long time.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 29 Jul 2013 23:21:38 +0300 |
parents | 77213cdc8d7f |
children | b73639f92610 |
files | src/director/director-connection.c src/director/director-host.h src/director/director.c src/director/director.h |
diffstat | 4 files changed, 51 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/src/director/director-connection.c Mon Jul 29 22:48:36 2013 +0300 +++ b/src/director/director-connection.c Mon Jul 29 23:21:38 2013 +0300 @@ -73,6 +73,7 @@ #define DIRECTOR_WAIT_DISCONNECT_SECS 10 #define DIRECTOR_HANDSHAKE_WARN_SECS 29 #define DIRECTOR_HANDSHAKE_BYTES_LOG_MIN_SECS (60*30) +#define DIRECTOR_MAX_SYNC_SEQ_DUPLICATES 4 #if DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS <= DIRECTOR_CONNECTION_PING_TIMEOUT_MSECS # error DIRECTOR_CONNECTION_DONE_TIMEOUT_MSECS is too low @@ -217,7 +218,7 @@ dir->sync_seq++; director_set_ring_unsynced(dir); director_sync_send(dir, dir->self_host, dir->sync_seq, - DIRECTOR_VERSION_MINOR); + DIRECTOR_VERSION_MINOR, ioloop_time); } director_connection_set_ping_timeout(conn); } @@ -1084,10 +1085,11 @@ return 0; } -static void +static bool director_connection_sync_host(struct director_connection *conn, struct director_host *host, - uint32_t seq, unsigned int minor_version) + uint32_t seq, unsigned int minor_version, + unsigned int timestamp) { struct director *dir = conn->dir; @@ -1099,7 +1101,7 @@ if (host->self) { if (dir->sync_seq != seq) { /* stale SYNC event */ - return; + return FALSE; } /* sync_seq increases when we get disconnected, so we must be successfully connected to both directions */ @@ -1117,10 +1119,28 @@ conn->name, seq); director_set_ring_synced(dir); } - } else if (dir->right != NULL) { - /* forward it to the connection on right */ - director_sync_send(dir, host, seq, minor_version); + } else { + if (seq < host->last_sync_seq) { + /* stale SYNC event */ + return FALSE; + } else if (host->last_sync_seq != seq || + timestamp < host->last_sync_timestamp) { + host->last_sync_seq = seq; + host->last_sync_timestamp = timestamp; + host->last_sync_seq_counter = 1; + } else if (++host->last_sync_seq_counter > + DIRECTOR_MAX_SYNC_SEQ_DUPLICATES) { + /* we've received this too many times already */ + return FALSE; + } + + if (dir->right != NULL) { + /* forward it to the connection on right */ + director_sync_send(dir, host, seq, minor_version, + timestamp); + } } + return TRUE; } static bool director_connection_sync(struct director_connection *conn, @@ -1129,7 +1149,7 @@ struct director *dir = conn->dir; struct director_host *host; struct ip_addr ip; - unsigned int port, seq, minor_version = 0; + unsigned int port, seq, minor_version = 0, timestamp = ioloop_time; if (str_array_length(args) < 3 || !director_args_parse_ip_port(conn, args, &ip, &port) || @@ -1137,18 +1157,25 @@ director_cmd_error(conn, "Invalid parameters"); return FALSE; } - if (args[3] != NULL) + if (args[3] != NULL) { minor_version = atoi(args[3]); + if (args[4] != NULL && str_to_uint(args[4], ×tamp) < 0) { + director_cmd_error(conn, "Invalid parameters"); + return FALSE; + } + } /* find the originating director. if we don't see it, it was already removed and we can ignore this sync. */ host = director_host_lookup(dir, &ip, port); if (host != NULL) { - director_connection_sync_host(conn, host, seq, - minor_version); + if (!director_connection_sync_host(conn, host, seq, + minor_version, timestamp)) + return TRUE; } - if (host == NULL || !host->self) + if ((host == NULL || !host->self) && + dir->self_host->last_sync_timestamp != ioloop_time) director_resend_sync(dir); return TRUE; }
--- a/src/director/director-host.h Mon Jul 29 22:48:36 2013 +0300 +++ b/src/director/director-host.h Mon Jul 29 23:21:38 2013 +0300 @@ -20,6 +20,9 @@ it can be ignored (or: it must be ignored to avoid potential command loops) */ unsigned int last_seq; + /* use these to avoid infinitely sending SYNCs for directors that + aren't connected in the ring. */ + unsigned int last_sync_seq, last_sync_seq_counter, last_sync_timestamp; /* Last time host was detected to be down */ time_t last_network_failure; time_t last_protocol_failure;
--- a/src/director/director.c Mon Jul 29 22:48:36 2013 +0300 +++ b/src/director/director.c Mon Jul 29 23:21:38 2013 +0300 @@ -302,7 +302,8 @@ } void director_sync_send(struct director *dir, struct director_host *host, - uint32_t seq, unsigned int minor_version) + uint32_t seq, unsigned int minor_version, + unsigned int timestamp) { string_t *str; @@ -311,8 +312,8 @@ net_ip2addr(&host->ip), host->port, seq); if (minor_version > 0 && director_connection_get_minor_version(dir->right) > 0) { - /* only minor_version>0 supports this parameter */ - str_printfa(str, "\t%u", minor_version); + /* only minor_version>0 supports extra parameters */ + str_printfa(str, "\t%u\t%u", minor_version, timestamp); } str_append_c(str, '\n'); director_connection_send(dir->right, str_c(str)); @@ -329,8 +330,9 @@ { if (!dir->ring_synced && dir->left != NULL && dir->right != NULL) { /* send a new SYNC in case the previous one got dropped */ + dir->self_host->last_sync_timestamp = ioloop_time; director_sync_send(dir, dir->self_host, dir->sync_seq, - DIRECTOR_VERSION_MINOR); + DIRECTOR_VERSION_MINOR, ioloop_time); if (dir->to_sync != NULL) timeout_reset(dir->to_sync); return TRUE; @@ -393,7 +395,7 @@ director_connection_set_synced(dir->left, FALSE); director_connection_set_synced(dir->right, FALSE); director_sync_send(dir, dir->self_host, dir->sync_seq, - DIRECTOR_VERSION_MINOR); + DIRECTOR_VERSION_MINOR, ioloop_time); } void director_sync_freeze(struct director *dir)
--- a/src/director/director.h Mon Jul 29 22:48:36 2013 +0300 +++ b/src/director/director.h Mon Jul 29 23:21:38 2013 +0300 @@ -103,7 +103,8 @@ void director_set_ring_unsynced(struct director *dir); void director_set_state_changed(struct director *dir); void director_sync_send(struct director *dir, struct director_host *host, - uint32_t seq, unsigned int minor_version); + uint32_t seq, unsigned int minor_version, + unsigned int timestamp); bool director_resend_sync(struct director *dir); void director_notify_ring_added(struct director_host *added_host,