From 9046db06c52b9a4413862af6954f951cc362b76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Tue, 19 Sep 2017 16:13:52 +0300 Subject: [PATCH] MXS-1295: Add `strict_sp_calls` parameter The new parameter allows the session to be "locked" to the master server after a stored procedure is called. This will keep the session state consistent if the stored procedure call modifies the state of the session. --- Documentation/Routers/ReadWriteSplit.md | 9 +++++++++ server/modules/routing/readwritesplit/readwritesplit.c | 8 ++++++++ server/modules/routing/readwritesplit/readwritesplit.h | 1 + .../modules/routing/readwritesplit/rwsplit_internal.h | 1 + .../routing/readwritesplit/rwsplit_route_stmt.c | 10 +++++++--- .../routing/readwritesplit/rwsplit_tmp_table_multi.c | 5 +++++ 6 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Documentation/Routers/ReadWriteSplit.md b/Documentation/Routers/ReadWriteSplit.md index c4cd6ac2c..90f756e4d 100644 --- a/Documentation/Routers/ReadWriteSplit.md +++ b/Documentation/Routers/ReadWriteSplit.md @@ -221,6 +221,15 @@ multi-statement queries. router_options=strict_multi_stmt=false ``` +### `strict_sp_calls` + +Similar to `strict_multi_stmt`, this option allows all queries after a CALL +operation on a stored procedure to be routed to the master. This option is +disabled by default and was added in MaxScale 2.1.9. + +All warnings and restrictions that apply to `strict_multi_stmt` also apply to +`strict_sp_calls`. + ### `master_failure_mode` This option controls how the failure of a master server is handled. By default, diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index ce15f6cad..e2c76158f 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -195,6 +195,7 @@ MXS_MODULE *MXS_CREATE_MODULE() {"disable_sescmd_history", MXS_MODULE_PARAM_BOOL, "true"}, {"max_sescmd_history", MXS_MODULE_PARAM_COUNT, "0"}, {"strict_multi_stmt", MXS_MODULE_PARAM_BOOL, "true"}, + {"strict_sp_calls", MXS_MODULE_PARAM_BOOL, "false"}, {"master_accept_reads", MXS_MODULE_PARAM_BOOL, "false"}, {MXS_END_MODULE_PARAMS} } @@ -275,6 +276,7 @@ static MXS_ROUTER *createInstance(SERVICE *service, char **options) router->rwsplit_config.max_slave_replication_lag = config_get_integer(params, "max_slave_replication_lag"); router->rwsplit_config.retry_failed_reads = config_get_bool(params, "retry_failed_reads"); router->rwsplit_config.strict_multi_stmt = config_get_bool(params, "strict_multi_stmt"); + router->rwsplit_config.strict_sp_calls = config_get_bool(params, "strict_sp_calls"); router->rwsplit_config.disable_sescmd_history = config_get_bool(params, "disable_sescmd_history"); router->rwsplit_config.max_sescmd_history = config_get_integer(params, "max_sescmd_history"); router->rwsplit_config.master_accept_reads = config_get_bool(params, "master_accept_reads"); @@ -606,6 +608,8 @@ static void diagnostics(MXS_ROUTER *instance, DCB *dcb) router->rwsplit_config.retry_failed_reads ? "true" : "false"); dcb_printf(dcb, "\tstrict_multi_stmt: %s\n", router->rwsplit_config.strict_multi_stmt ? "true" : "false"); + dcb_printf(dcb, "\tstrict_sp_calls: %s\n", + router->rwsplit_config.strict_sp_calls ? "true" : "false"); dcb_printf(dcb, "\tdisable_sescmd_history: %s\n", router->rwsplit_config.disable_sescmd_history ? "true" : "false"); dcb_printf(dcb, "\tmax_sescmd_history: %d\n", @@ -1187,6 +1191,10 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router, { router->rwsplit_config.strict_multi_stmt = config_truth_value(value); } + else if (strcmp(options[i], "strict_sp_calls") == 0) + { + router->rwsplit_config.strict_sp_calls = config_truth_value(value); + } else if (strcmp(options[i], "retry_failed_reads") == 0) { router->rwsplit_config.retry_failed_reads = config_truth_value(value); diff --git a/server/modules/routing/readwritesplit/readwritesplit.h b/server/modules/routing/readwritesplit/readwritesplit.h index 671bd9f9c..d93cb5ed7 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.h +++ b/server/modules/routing/readwritesplit/readwritesplit.h @@ -272,6 +272,7 @@ typedef struct rwsplit_config_st bool master_accept_reads; /**< Use master for reads */ bool strict_multi_stmt; /**< Force non-multistatement queries to be routed * to the master after a multistatement query. */ + bool strict_sp_calls; /**< Lock session to master after an SP call */ enum failure_mode master_failure_mode; /**< Master server failure handling mode. * @see enum failure_mode */ bool retry_failed_reads; /**< Retry failed reads on other servers */ diff --git a/server/modules/routing/readwritesplit/rwsplit_internal.h b/server/modules/routing/readwritesplit/rwsplit_internal.h index 78f415218..ce11fefef 100644 --- a/server/modules/routing/readwritesplit/rwsplit_internal.h +++ b/server/modules/routing/readwritesplit/rwsplit_internal.h @@ -146,6 +146,7 @@ bool is_read_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, void check_create_tmp_table(ROUTER_CLIENT_SES *router_cli_ses, GWBUF *querybuf, qc_query_type_t type); bool check_for_multi_stmt(GWBUF *buf, void *protocol, mysql_server_cmd_t packet_type); +bool check_for_sp_call(GWBUF *buf, mysql_server_cmd_t packet_type); qc_query_type_t determine_query_type(GWBUF *querybuf, int packet_type, bool non_empty_packet); void close_failed_bref(backend_ref_t *bref, bool fatal); diff --git a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c index b8352215e..9bb12a7ba 100644 --- a/server/modules/routing/readwritesplit/rwsplit_route_stmt.c +++ b/server/modules/routing/readwritesplit/rwsplit_route_stmt.c @@ -134,9 +134,11 @@ bool route_single_stmt(ROUTER_INSTANCE *inst, ROUTER_CLIENT_SES *rses, succp = handle_master_is_target(inst, rses, &target_dcb); if (!rses->rses_config.strict_multi_stmt && + !rses->rses_config.strict_sp_calls && rses->forced_node == rses->rses_master_ref) { - /** Reset the forced node as we're in relaxed multi-statement mode */ + /** Reset the forced node as we're in relaxed multi-statement + * and SP call mode */ rses->forced_node = NULL; } } @@ -892,12 +894,14 @@ handle_multi_temp_and_load(ROUTER_CLIENT_SES *rses, GWBUF *querybuf, * situation, assigning QUERY_TYPE_WRITE for the query will trigger * the error processing. */ if ((rses->forced_node == NULL || rses->forced_node != rses->rses_master_ref) && - check_for_multi_stmt(querybuf, rses->client_dcb->protocol, packet_type)) + (check_for_multi_stmt(querybuf, rses->client_dcb->protocol, packet_type) || + check_for_sp_call(querybuf, packet_type))) { if (rses->rses_master_ref) { rses->forced_node = rses->rses_master_ref; - MXS_INFO("Multi-statement query, routing all future queries to master."); + MXS_INFO("Multi-statement query or stored procedure call, routing " + "all future queries to master."); } else { diff --git a/server/modules/routing/readwritesplit/rwsplit_tmp_table_multi.c b/server/modules/routing/readwritesplit/rwsplit_tmp_table_multi.c index ecdd0e2a4..d9e52f50c 100644 --- a/server/modules/routing/readwritesplit/rwsplit_tmp_table_multi.c +++ b/server/modules/routing/readwritesplit/rwsplit_tmp_table_multi.c @@ -355,6 +355,11 @@ bool check_for_multi_stmt(GWBUF *buf, void *protocol, mysql_server_cmd_t packet_ return rval; } +bool check_for_sp_call(GWBUF *buf, mysql_server_cmd_t packet_type) +{ + return packet_type == MYSQL_COM_QUERY && qc_get_operation(buf) == QUERY_OP_CALL; +} + /** * @brief Determine the type of a query *