Mercurial > dovecot > core-2.2
changeset 20552:cc06003a338b
imap-hibernate: If imap-master socket is busy, retry un-hibernation later.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Tue, 19 Jul 2016 10:16:17 -0600 |
parents | 64f547f5bd86 |
children | 63a54b6d58a1 |
files | src/imap-hibernate/imap-client.c src/imap-hibernate/imap-client.h src/imap-hibernate/imap-master-connection.c src/imap-hibernate/imap-master-connection.h src/imap-hibernate/main.c |
diffstat | 5 files changed, 121 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap-hibernate/imap-client.c Tue Aug 02 13:41:07 2016 +0300 +++ b/src/imap-hibernate/imap-client.c Tue Jul 19 10:16:17 2016 -0600 @@ -11,6 +11,7 @@ #include "istream.h" #include "ostream.h" #include "llist.h" +#include "priorityq.h" #include "base64.h" #include "str.h" #include "strescape.h" @@ -28,6 +29,16 @@ /* we only need enough for "DONE\r\nIDLE\r\n" */ #define IMAP_MAX_INBUF 12 +/* If client has sent input and we can't recreate imap process in this + many seconds, disconnect the client. */ +#define IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS 10 +/* If there's a change notification and we can't recreate imap process in this + many seconds, disconnect the client. */ +#define IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS (60*5) + +/* How often to try to unhibernate clients. */ +#define IMAP_UNHIBERNATE_RETRY_MSECS 10 + enum imap_client_input_state { IMAP_CLIENT_INPUT_STATE_UNKNOWN, IMAP_CLIENT_INPUT_STATE_BAD, @@ -42,11 +53,16 @@ }; struct imap_client { + struct priorityq_item item; + struct imap_client *prev, *next; pool_t pool; struct imap_client_state state; ARRAY(struct imap_client_notify) notifys; + time_t move_back_start; + struct timeout *to_move_back; + int fd; struct io *io; struct istream *input; @@ -57,14 +73,19 @@ unsigned int imap_still_here_text_pos; unsigned int next_read_threshold; bool bad_done, idle_done; + bool unhibernate_queued; + bool input_pending; }; static struct imap_client *imap_clients; +static struct priorityq *unhibernate_queue; +static struct timeout *to_unhibernate; static const char imap_still_here_text[] = "* OK Still here\r\n"; static void imap_client_stop(struct imap_client *client); void imap_client_destroy(struct imap_client **_client, const char *reason); static void imap_client_add_idle_keepalive_timeout(struct imap_client *client); +static void imap_clients_unhibernate(void *context); static void imap_client_disconnected(struct imap_client **_client) { @@ -174,22 +195,53 @@ } } -static void imap_client_move_back(struct imap_client *client) +static bool imap_client_try_move_back(struct imap_client *client) { const struct master_service_settings *master_set; - const char *path; - - imap_client_stop(client); + const char *path, *error; + int ret; master_set = master_service_settings_get(master_service); path = t_strconcat(master_set->base_dir, "/"IMAP_MASTER_SOCKET_NAME, NULL); - if (imap_master_connection_init(path, - imap_client_move_back_send_callback, - imap_client_move_back_read_callback, - client, &client->master_conn) < 0) { + ret = imap_master_connection_init(path, + imap_client_move_back_send_callback, + imap_client_move_back_read_callback, + client, &client->master_conn, &error); + if (ret > 0) { + /* success */ + imap_client_stop(client); + return TRUE; + } else if (ret < 0) { /* failed to connect to the imap-master socket */ - imap_client_destroy(&client, "Failed to connect to master socket"); + imap_client_destroy(&client, error); + return TRUE; + } + + int max_secs = client->input_pending ? + IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : + IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS; + if (ioloop_time - client->move_back_start > max_secs) { + /* we've waited long enough */ + imap_client_destroy(&client, error); + return TRUE; + } + return FALSE; +} + +static void imap_client_move_back(struct imap_client *client) +{ + if (imap_client_try_move_back(client)) + return; + + /* imap-master socket is busy. retry in a while. */ + if (client->move_back_start == 0) + client->move_back_start = ioloop_time; + client->unhibernate_queued = TRUE; + priorityq_add(unhibernate_queue, &client->item); + if (to_unhibernate == NULL) { + to_unhibernate = timeout_add_short(IMAP_UNHIBERNATE_RETRY_MSECS, + imap_clients_unhibernate, NULL); } } @@ -269,6 +321,7 @@ return; } client->idle_done = TRUE; + client->input_pending = TRUE; imap_client_move_back(client); } @@ -276,8 +329,10 @@ { if (i_stream_read(client->input) < 0) imap_client_disconnected(&client); - else + else { + client->input_pending = TRUE; imap_client_move_back(client); + } } static void imap_client_input_notify(struct imap_client *client) @@ -459,6 +514,8 @@ { struct imap_client_notify *notify; + if (client->unhibernate_queued) + priorityq_remove(unhibernate_queue, &client->item); if (client->io != NULL) io_remove(&client->io); if (client->to_keepalive != NULL) @@ -541,6 +598,44 @@ } } +static int client_unhibernate_cmp(const void *p1, const void *p2) +{ + const struct imap_client *c1 = p1, *c2 = p2; + time_t t1, t2; + + t1 = c1->move_back_start + + (c1->input_pending ? + IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : + IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS); + t2 = c2->move_back_start + + (c1->input_pending ? + IMAP_CLIENT_MOVE_BACK_WITH_INPUT_TIMEOUT_SECS : + IMAP_CLIENT_MOVE_BACK_WITHOUT_INPUT_TIMEOUT_SECS); + if (t1 < t2) + return -1; + if (t1 > t2) + return 1; + return 0; +} + +static void imap_clients_unhibernate(void *context ATTR_UNUSED) +{ + struct priorityq_item *item; + + while ((item = priorityq_pop(unhibernate_queue)) != NULL) { + struct imap_client *client = (struct imap_client *)item; + + if (!imap_client_try_move_back(client)) + return; + } + timeout_remove(&to_unhibernate); +} + +void imap_clients_init(void) +{ + unhibernate_queue = priorityq_init(client_unhibernate_cmp, 64); +} + void imap_clients_deinit(void) { while (imap_clients != NULL) { @@ -549,4 +644,7 @@ imap_client_io_activate_user(client); imap_client_destroy(&client, "Shutting down"); } + if (to_unhibernate != NULL) + timeout_remove(&to_unhibernate); + priorityq_deinit(&unhibernate_queue); }
--- a/src/imap-hibernate/imap-client.h Tue Aug 02 13:41:07 2016 +0300 +++ b/src/imap-hibernate/imap-client.h Tue Jul 19 10:16:17 2016 -0600 @@ -31,6 +31,7 @@ void imap_client_create_finish(struct imap_client *client); void imap_client_destroy(struct imap_client **_client, const char *reason); +void imap_clients_init(void); void imap_clients_deinit(void); #endif
--- a/src/imap-hibernate/imap-master-connection.c Tue Aug 02 13:41:07 2016 +0300 +++ b/src/imap-hibernate/imap-master-connection.c Tue Jul 19 10:16:17 2016 -0600 @@ -28,7 +28,8 @@ imap_master_connection_send_callback_t *send_callback, imap_master_connection_read_callback_t *read_callback, void *context, - struct imap_master_connection **conn_r) + struct imap_master_connection **conn_r, + const char **error_r) { struct imap_master_connection *conn; @@ -38,15 +39,18 @@ conn->context = context; connection_init_client_unix(master_clients, &conn->conn, path); if (connection_client_connect(&conn->conn) < 0) { - i_error("net_connect_unix(%s) failed: %m", path); + int ret = errno == EAGAIN ? 0 : -1; + + *error_r = t_strdup_printf( + "net_connect_unix(%s) failed: %m", path); connection_deinit(&conn->conn); i_free(conn); - return -1; + return ret; } conn->to = timeout_add(IMAP_MASTER_CONNECTION_TIMEOUT_MSECS, imap_master_connection_timeout, conn); *conn_r = conn; - return 0; + return 1; } void imap_master_connection_deinit(struct imap_master_connection **_conn)
--- a/src/imap-hibernate/imap-master-connection.h Tue Aug 02 13:41:07 2016 +0300 +++ b/src/imap-hibernate/imap-master-connection.h Tue Jul 19 10:16:17 2016 -0600 @@ -8,11 +8,13 @@ typedef void imap_master_connection_read_callback_t(void *context, const char *reply); +/* Returns 1 = success, 0 = retry later, -1 = error */ int imap_master_connection_init(const char *path, imap_master_connection_send_callback_t *send_callback, imap_master_connection_read_callback_t *read_callback, void *context, - struct imap_master_connection **conn_r); + struct imap_master_connection **conn_r, + const char **error_r); void imap_master_connection_deinit(struct imap_master_connection **conn); void imap_master_connections_init(void);
--- a/src/imap-hibernate/main.c Tue Aug 02 13:41:07 2016 +0300 +++ b/src/imap-hibernate/main.c Tue Jul 19 10:16:17 2016 -0600 @@ -42,6 +42,7 @@ restrict_access_by_env(NULL, FALSE); restrict_access_allow_coredumps(TRUE); + imap_clients_init(); imap_master_connections_init(); imap_hibernate_clients_init(); master_service_init_finish(master_service);