comparison src/director/doveadm-connection.c @ 22536:5f09f6aa089b

director: doveadm HOST-* commands now wait for ring sync before returning OK This should make it easier for tests and maybe for scripts in general, so they won't think the command failed when it just takes a while to finish.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 14 Aug 2017 10:29:47 +0300
parents 2805abc195e1
children 4e3e963c32f0
comparison
equal deleted inserted replaced
22535:2805abc195e1 22536:5f09f6aa089b
23 #define DOVEADM_PROTOCOL_VERSION_MAJOR 1 23 #define DOVEADM_PROTOCOL_VERSION_MAJOR 1
24 #define DOVEADM_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n" 24 #define DOVEADM_HANDSHAKE "VERSION\tdirector-doveadm\t1\t0\n"
25 25
26 #define MAX_VALID_VHOST_COUNT 1000 26 #define MAX_VALID_VHOST_COUNT 1000
27 #define DEFAULT_MAX_MOVING_USERS 100 27 #define DEFAULT_MAX_MOVING_USERS 100
28 #define DOVEADM_CONNECTION_RING_SYNC_TIMEOUT_MSECS (30*1000)
28 29
29 enum doveadm_director_cmd_ret { 30 enum doveadm_director_cmd_ret {
30 DOVEADM_DIRECTOR_CMD_RET_FAIL = -1, 31 DOVEADM_DIRECTOR_CMD_RET_FAIL = -1,
31 DOVEADM_DIRECTOR_CMD_RET_UNFINISHED = 0, 32 DOVEADM_DIRECTOR_CMD_RET_UNFINISHED = 0,
32 DOVEADM_DIRECTOR_CMD_RET_OK = 1, 33 DOVEADM_DIRECTOR_CMD_RET_OK = 1,
34 DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK,
33 }; 35 };
36
37 typedef void
38 doveadm_connection_ring_sync_callback_t(struct doveadm_connection *);
34 39
35 struct director_reset_cmd { 40 struct director_reset_cmd {
36 struct director_reset_cmd *prev, *next; 41 struct director_reset_cmd *prev, *next;
37 42
38 struct director *dir; 43 struct director *dir;
49 struct io *io; 54 struct io *io;
50 struct istream *input; 55 struct istream *input;
51 struct ostream *output; 56 struct ostream *output;
52 struct director *dir; 57 struct director *dir;
53 58
59 struct timeout *to_ring_sync_abort;
54 struct director_reset_cmd *reset_cmd; 60 struct director_reset_cmd *reset_cmd;
61 doveadm_connection_ring_sync_callback_t *ring_sync_callback;
55 62
56 unsigned int handshaked:1; 63 unsigned int handshaked:1;
57 }; 64 };
58 65
59 static struct doveadm_connection *doveadm_connections; 66 static struct doveadm_connection *doveadm_connections;
67 static struct doveadm_connection *doveadm_ring_sync_pending_connections;
60 static struct director_reset_cmd *reset_cmds = NULL; 68 static struct director_reset_cmd *reset_cmds = NULL;
61 69
62 static void doveadm_connection_set_io(struct doveadm_connection *conn); 70 static void doveadm_connection_set_io(struct doveadm_connection *conn);
63 static void doveadm_connection_deinit(struct doveadm_connection **_conn); 71 static void doveadm_connection_deinit(struct doveadm_connection **_conn);
72 static void
73 doveadm_connection_ring_sync_list_move(struct doveadm_connection *conn);
64 74
65 static enum doveadm_director_cmd_ret 75 static enum doveadm_director_cmd_ret
66 doveadm_cmd_host_list(struct doveadm_connection *conn, 76 doveadm_cmd_host_list(struct doveadm_connection *conn,
67 const char *const *args ATTR_UNUSED) 77 const char *const *args ATTR_UNUSED)
68 { 78 {
324 /* NOTE: we don't support changing a tag for an existing host. 334 /* NOTE: we don't support changing a tag for an existing host.
325 it needs to be removed first. otherwise it would be a bit ugly to 335 it needs to be removed first. otherwise it would be a bit ugly to
326 handle. */ 336 handle. */
327 director_update_host(dir, dir->self_host, NULL, host); 337 director_update_host(dir, dir->self_host, NULL, host);
328 338
329 o_stream_nsend(conn->output, "OK\n", 3); 339 return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK;
330 return DOVEADM_DIRECTOR_CMD_RET_OK;
331 } 340 }
332 341
333 static enum doveadm_director_cmd_ret 342 static enum doveadm_director_cmd_ret
334 doveadm_cmd_host_set(struct doveadm_connection *conn, const char *const *args) 343 doveadm_cmd_host_set(struct doveadm_connection *conn, const char *const *args)
335 { 344 {
358 host = mail_host_lookup(conn->dir->mail_hosts, &ip); 367 host = mail_host_lookup(conn->dir->mail_hosts, &ip);
359 if (host == NULL) { 368 if (host == NULL) {
360 o_stream_nsend_str(conn->output, "NOTFOUND\n"); 369 o_stream_nsend_str(conn->output, "NOTFOUND\n");
361 return DOVEADM_DIRECTOR_CMD_RET_OK; 370 return DOVEADM_DIRECTOR_CMD_RET_OK;
362 } 371 }
363 if (host->down == down) 372 if (host->down == down) {
364 ; 373 o_stream_nsend_str(conn->output, "OK\n");
365 else if (host->desynced) { 374 return DOVEADM_DIRECTOR_CMD_RET_OK;
375 } else if (host->desynced) {
366 o_stream_nsend_str(conn->output, 376 o_stream_nsend_str(conn->output,
367 "host is already being updated - try again later\n"); 377 "host is already being updated - try again later\n");
368 return DOVEADM_DIRECTOR_CMD_RET_OK; 378 return DOVEADM_DIRECTOR_CMD_RET_OK;
369 } else { 379 } else {
370 mail_host_set_down(host, down, ioloop_time, "doveadm: "); 380 mail_host_set_down(host, down, ioloop_time, "doveadm: ");
371 director_update_host(conn->dir, conn->dir->self_host, 381 director_update_host(conn->dir, conn->dir->self_host,
372 NULL, host); 382 NULL, host);
373 } 383 return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK;
374 o_stream_nsend(conn->output, "OK\n", 3); 384 }
375 return DOVEADM_DIRECTOR_CMD_RET_OK;
376 } 385 }
377 386
378 static enum doveadm_director_cmd_ret 387 static enum doveadm_director_cmd_ret
379 doveadm_cmd_host_up(struct doveadm_connection *conn, 388 doveadm_cmd_host_up(struct doveadm_connection *conn,
380 const char *const *args) 389 const char *const *args)
399 if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) { 408 if (args[0] == NULL || net_addr2ip(args[0], &ip) < 0) {
400 i_error("doveadm sent invalid HOST-REMOVE parameters"); 409 i_error("doveadm sent invalid HOST-REMOVE parameters");
401 return DOVEADM_DIRECTOR_CMD_RET_FAIL; 410 return DOVEADM_DIRECTOR_CMD_RET_FAIL;
402 } 411 }
403 host = mail_host_lookup(conn->dir->mail_hosts, &ip); 412 host = mail_host_lookup(conn->dir->mail_hosts, &ip);
404 if (host == NULL) 413 if (host == NULL) {
405 o_stream_nsend_str(conn->output, "NOTFOUND\n"); 414 o_stream_nsend_str(conn->output, "NOTFOUND\n");
406 else { 415 return DOVEADM_DIRECTOR_CMD_RET_OK;
416 } else {
407 director_remove_host(conn->dir, conn->dir->self_host, 417 director_remove_host(conn->dir, conn->dir->self_host,
408 NULL, host); 418 NULL, host);
409 o_stream_nsend(conn->output, "OK\n", 3); 419 return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK;
410 } 420 }
411 return DOVEADM_DIRECTOR_CMD_RET_OK;
412 } 421 }
413 422
414 static void 423 static void
415 doveadm_cmd_host_flush_all(struct doveadm_connection *conn) 424 doveadm_cmd_host_flush_all(struct doveadm_connection *conn)
416 { 425 {
442 if (net_addr2ip(args[0], &ip) < 0) { 451 if (net_addr2ip(args[0], &ip) < 0) {
443 i_error("doveadm sent invalid HOST-FLUSH parameters"); 452 i_error("doveadm sent invalid HOST-FLUSH parameters");
444 return DOVEADM_DIRECTOR_CMD_RET_FAIL; 453 return DOVEADM_DIRECTOR_CMD_RET_FAIL;
445 } 454 }
446 host = mail_host_lookup(conn->dir->mail_hosts, &ip); 455 host = mail_host_lookup(conn->dir->mail_hosts, &ip);
447 if (host == NULL) 456 if (host == NULL) {
448 o_stream_nsend_str(conn->output, "NOTFOUND\n"); 457 o_stream_nsend_str(conn->output, "NOTFOUND\n");
449 else { 458 return DOVEADM_DIRECTOR_CMD_RET_OK;
459 } else {
450 director_flush_host(conn->dir, conn->dir->self_host, 460 director_flush_host(conn->dir, conn->dir->self_host,
451 NULL, host); 461 NULL, host);
452 o_stream_nsend(conn->output, "OK\n", 3); 462 return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK;
453 } 463 }
454 return DOVEADM_DIRECTOR_CMD_RET_OK;
455 } 464 }
456 465
457 static void doveadm_reset_cmd_free(struct director_reset_cmd *cmd) 466 static void doveadm_reset_cmd_free(struct director_reset_cmd *cmd)
458 { 467 {
459 DLLIST_REMOVE(&reset_cmds, cmd); 468 DLLIST_REMOVE(&reset_cmds, cmd);
769 { "USER-MOVE", doveadm_cmd_user_move }, 778 { "USER-MOVE", doveadm_cmd_user_move },
770 { "USER-KICK", doveadm_cmd_user_kick }, 779 { "USER-KICK", doveadm_cmd_user_kick },
771 { "USER-KICK-ALT", doveadm_cmd_user_kick_alt }, 780 { "USER-KICK-ALT", doveadm_cmd_user_kick_alt },
772 }; 781 };
773 782
783 static void
784 doveadm_connection_ring_sync_timeout(struct doveadm_connection *conn)
785 {
786 doveadm_connection_ring_sync_list_move(conn);
787 o_stream_nsend_str(conn->output, "Ring sync timed out\n");
788
789 doveadm_connection_set_io(conn);
790 io_set_pending(conn->io);
791 }
792
793 static void
794 doveadm_connection_set_ring_sync_callback(struct doveadm_connection *conn,
795 doveadm_connection_ring_sync_callback_t *callback)
796 {
797 i_assert(conn->ring_sync_callback == NULL);
798 i_assert(conn->to_ring_sync_abort == NULL);
799
800 conn->ring_sync_callback = callback;
801 io_remove(&conn->io);
802 DLLIST_REMOVE(&doveadm_connections, conn);
803 DLLIST_PREPEND(&doveadm_ring_sync_pending_connections, conn);
804 conn->to_ring_sync_abort =
805 timeout_add(DOVEADM_CONNECTION_RING_SYNC_TIMEOUT_MSECS,
806 doveadm_connection_ring_sync_timeout, conn);
807 }
808
809 static void doveadm_connection_ret_ok(struct doveadm_connection *conn)
810 {
811 o_stream_nsend(conn->output, "OK\n", 3);
812 }
813
814 static enum doveadm_director_cmd_ret
815 doveadm_connection_cmd_run(struct doveadm_connection *conn,
816 const char *const *args, unsigned int i)
817 {
818 enum doveadm_director_cmd_ret ret;
819
820 ret = doveadm_director_commands[i].cmd(conn, args);
821 if (ret != DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK)
822 return ret;
823 /* Delay sending OK until ring is synced. This way doveadm will know
824 whether the call actually succeeded or not. */
825 if (conn->dir->ring_synced) {
826 /* director is alone */
827 i_assert(conn->dir->right == NULL && conn->dir->left == NULL);
828 o_stream_nsend(conn->output, "OK\n", 3);
829 return DOVEADM_DIRECTOR_CMD_RET_OK;
830 }
831 doveadm_connection_set_ring_sync_callback(conn, doveadm_connection_ret_ok);
832 return DOVEADM_DIRECTOR_CMD_RET_RING_SYNC_OK;
833 }
834
774 static enum doveadm_director_cmd_ret 835 static enum doveadm_director_cmd_ret
775 doveadm_connection_cmd(struct doveadm_connection *conn, const char *line) 836 doveadm_connection_cmd(struct doveadm_connection *conn, const char *line)
776 { 837 {
777 const char *cmd, *const *args; 838 const char *cmd, *const *args;
778 839
784 cmd = args[0]; 845 cmd = args[0];
785 args++; 846 args++;
786 847
787 for (unsigned int i = 0; i < N_ELEMENTS(doveadm_director_commands); i++) { 848 for (unsigned int i = 0; i < N_ELEMENTS(doveadm_director_commands); i++) {
788 if (strcmp(doveadm_director_commands[i].name, cmd) == 0) 849 if (strcmp(doveadm_director_commands[i].name, cmd) == 0)
789 return doveadm_director_commands[i].cmd(conn, args); 850 return doveadm_connection_cmd_run(conn, args, i);
790 } 851 }
791 i_error("doveadm sent unknown command: %s", line); 852 i_error("doveadm sent unknown command: %s", line);
792 return DOVEADM_DIRECTOR_CMD_RET_FAIL; 853 return DOVEADM_DIRECTOR_CMD_RET_FAIL;
793 } 854 }
794 855
852 { 913 {
853 struct doveadm_connection *conn = *_conn; 914 struct doveadm_connection *conn = *_conn;
854 915
855 *_conn = NULL; 916 *_conn = NULL;
856 917
918 i_assert(conn->to_ring_sync_abort == NULL);
919
857 if (conn->reset_cmd != NULL) { 920 if (conn->reset_cmd != NULL) {
858 /* finish the move even if doveadm disconnected */ 921 /* finish the move even if doveadm disconnected */
859 conn->reset_cmd->_conn = NULL; 922 conn->reset_cmd->_conn = NULL;
860 } 923 }
861 924
868 i_free(conn); 931 i_free(conn);
869 932
870 master_service_client_connection_destroyed(master_service); 933 master_service_client_connection_destroyed(master_service);
871 } 934 }
872 935
936 static void
937 doveadm_connection_ring_sync_list_move(struct doveadm_connection *conn)
938 {
939 timeout_remove(&conn->to_ring_sync_abort);
940 DLLIST_REMOVE(&doveadm_ring_sync_pending_connections, conn);
941 DLLIST_PREPEND(&doveadm_connections, conn);
942 }
943
873 void doveadm_connections_deinit(void) 944 void doveadm_connections_deinit(void)
874 { 945 {
875 while (reset_cmds != NULL) 946 while (reset_cmds != NULL)
876 doveadm_reset_cmd_free(reset_cmds); 947 doveadm_reset_cmd_free(reset_cmds);
948
949 unsigned int pending_count = 0;
950 while (doveadm_ring_sync_pending_connections != NULL) {
951 doveadm_connection_ring_sync_list_move(doveadm_ring_sync_pending_connections);
952 pending_count++;
953 }
954 if (pending_count > 0)
955 i_warning("Shutting down while %u doveadm connections were waiting for ring sync", pending_count);
877 while (doveadm_connections != NULL) { 956 while (doveadm_connections != NULL) {
878 struct doveadm_connection *conn = doveadm_connections; 957 struct doveadm_connection *conn = doveadm_connections;
879 958
880 doveadm_connection_deinit(&conn); 959 doveadm_connection_deinit(&conn);
881 } 960 }
882 } 961 }
883 962
884 void doveadm_connections_continue_reset_cmds(void) 963 static void doveadm_connections_continue_reset_cmds(void)
885 { 964 {
886 while (reset_cmds != NULL) { 965 while (reset_cmds != NULL) {
887 if (!director_reset_cmd_run(reset_cmds)) 966 if (!director_reset_cmd_run(reset_cmds))
888 break; 967 break;
889 } 968 }
890 } 969 }
970
971 void doveadm_connections_ring_synced(void)
972 {
973 while (doveadm_ring_sync_pending_connections != NULL) {
974 struct doveadm_connection *conn =
975 doveadm_ring_sync_pending_connections;
976 doveadm_connection_ring_sync_callback_t *callback =
977 conn->ring_sync_callback;
978
979 conn->ring_sync_callback = NULL;
980 doveadm_connection_ring_sync_list_move(conn);
981 doveadm_connection_set_io(conn);
982 io_set_pending(conn->io);
983 callback(conn);
984 }
985 doveadm_connections_continue_reset_cmds();
986 }