Mercurial > dovecot > core-2.2
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 } |