diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 859be38fa..02d693ac1 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -406,9 +406,9 @@ return_here: * restrictive, for example, QUERY_TYPE_READ is smaller than QUERY_TYPE_WRITE. * */ -static u_int16_t set_query_type( - u_int16_t* qtype, - u_int16_t new_type) +static u_int32_t set_query_type( + u_int32_t* qtype, + u_int32_t new_type) { *qtype = MAX(*qtype, new_type); return *qtype; @@ -434,7 +434,7 @@ static skygw_query_type_t resolve_query_type( THD* thd) { skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN; - u_int16_t type = QUERY_TYPE_UNKNOWN; + u_int32_t type = QUERY_TYPE_UNKNOWN; int set_autocommit_stmt = -1; /*< -1 no, 0 disable, 1 enable */ LEX* lex; Item* item; @@ -501,27 +501,51 @@ static skygw_query_type_t resolve_query_type( type |= QUERY_TYPE_DISABLE_AUTOCOMMIT; type |= QUERY_TYPE_BEGIN_TRX; } - /** - * REVOKE ALL, ASSIGN_TO_KEYCACHE, - * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER - */ + if (lex->option_type == OPT_GLOBAL) { - type |= QUERY_TYPE_GLOBAL_WRITE; - goto return_qtype; + /** + * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html + */ + if (lex->sql_command == SQLCOM_SHOW_VARIABLES) + { + type |= QUERY_TYPE_GSYSVAR_READ; + } + /** + * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html + */ + else if (lex->sql_command == SQLCOM_SET_OPTION) + { + type |= QUERY_TYPE_GSYSVAR_WRITE; + } + /** + * REVOKE ALL, ASSIGN_TO_KEYCACHE, + * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER + */ + else + { + type |= QUERY_TYPE_GSYSVAR_WRITE; + } + goto return_qtype; } else if (lex->option_type == OPT_SESSION) { - /** SHOW commands are all reads to one backend */ + /** + * SHOW syntax http://dev.mysql.com/doc/refman/5.6/en/show.html + */ if (lex->sql_command == SQLCOM_SHOW_VARIABLES) { - type |= QUERY_TYPE_SESSION_READ; + type |= QUERY_TYPE_SYSVAR_READ; } - else + /** + * SET syntax http://dev.mysql.com/doc/refman/5.6/en/set-statement.html + */ + else if (lex->sql_command == SQLCOM_SET_OPTION) { - type |= QUERY_TYPE_SESSION_WRITE; + /** Either user- or system variable write */ + type |= QUERY_TYPE_SESSION_WRITE; } - goto return_qtype; + goto return_qtype; } /** * 1:ALTER TABLE, TRUNCATE, REPAIR, OPTIMIZE, ANALYZE, CHECK. @@ -538,31 +562,26 @@ static skygw_query_type_t resolve_query_type( if (thd->variables.sql_log_bin == 0 && force_data_modify_op_replication) { + /** Not replicated */ type |= QUERY_TYPE_SESSION_WRITE; } else { - type |= QUERY_TYPE_WRITE; + /** Written to binlog, that is, replicated except tmp tables */ + type |= QUERY_TYPE_WRITE; /*< to master */ if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE && lex->sql_command == SQLCOM_CREATE_TABLE) { - type |= QUERY_TYPE_CREATE_TMP_TABLE; - } - + type |= QUERY_TYPE_CREATE_TMP_TABLE; /*< remember in router */ + } } goto return_qtype; } /** Try to catch session modifications here */ switch (lex->sql_command) { - case SQLCOM_SET_OPTION: /*< SET commands. */ - if (lex->option_type == OPT_GLOBAL) - { - type |= QUERY_TYPE_GLOBAL_WRITE; - break; - } - /**name != NULL && strcmp(item->name, "last_insert_id()") == 0) { - func_qtype |= QUERY_TYPE_SESSION_READ; + func_qtype |= QUERY_TYPE_MASTER_READ; } else { @@ -757,6 +802,7 @@ static skygw_query_type_t resolve_query_type( /**< Set new query type */ type |= set_query_type(&type, func_qtype); } +#if defined(UPDATE_VAR_SUPPORT) /** * Write is as restrictive as it gets due functions, * so break. @@ -764,8 +810,9 @@ static skygw_query_type_t resolve_query_type( if ((type & QUERY_TYPE_WRITE) == QUERY_TYPE_WRITE) { break; } +#endif } /**< for */ - } /**< if */ + } /**< if */ return_qtype: qtype = (skygw_query_type_t)type; return qtype; diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index 92aaf6b92..c92ac6286 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -25,29 +25,39 @@ Copyright SkySQL Ab EXTERN_C_BLOCK_BEGIN +bool allow_var_writes_to_slaves = false; +bool allow_var_reads_from_slaves = false; + /** * Query type for skygateway. * The meaninful difference is where operation is done and whether master data * is modified */ typedef enum { - QUERY_TYPE_UNKNOWN = 0x0000, /*< Initial value, can't be tested bitwisely */ - QUERY_TYPE_LOCAL_READ = 0x0001, /*< Read non-database data, execute in MaxScale */ - QUERY_TYPE_READ = 0x0002, /*< No updates */ - QUERY_TYPE_WRITE = 0x0004, /*< Master data will be modified */ - QUERY_TYPE_SESSION_WRITE = 0x0008, /*< Session data will be modified */ - QUERY_TYPE_GLOBAL_WRITE = 0x0010, /*< Global system variable modification */ - QUERY_TYPE_BEGIN_TRX = 0x0020, /*< BEGIN or START TRANSACTION */ - QUERY_TYPE_ENABLE_AUTOCOMMIT = 0x0040, /*< SET autocommit=1 */ - QUERY_TYPE_DISABLE_AUTOCOMMIT = 0x0080, /*< SET autocommit=0 */ - QUERY_TYPE_ROLLBACK = 0x0100, /*< ROLLBACK */ - QUERY_TYPE_COMMIT = 0x0200, /*< COMMIT */ - QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */ - QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */ - QUERY_TYPE_EXEC_STMT = 0x1000, /*< Execute prepared statement */ - QUERY_TYPE_SESSION_READ = 0x2000, /*< Read session data (from master 31.8.14) */ - QUERY_TYPE_CREATE_TMP_TABLE = 0x4000, /*< Create temporary table */ - QUERY_TYPE_READ_TMP_TABLE = 0x8000 /*< Read temporary table */ + QUERY_TYPE_UNKNOWN = 0x000000, /*< Initial value, can't be tested bitwisely */ + QUERY_TYPE_LOCAL_READ = 0x000001, /*< Read non-database data, execute in MaxScale:any */ + QUERY_TYPE_READ = 0x000002, /*< Read database data:any */ + QUERY_TYPE_WRITE = 0x000004, /*< Master data will be modified:master */ + QUERY_TYPE_MASTER_READ = 0x000008, /*< Read from the master:master */ + QUERY_TYPE_SESSION_WRITE = 0x000010, /*< Session data will be modified:master or all */ + /** Not implemented yet */ +// QUERY_TYPE_USERVAR_WRITE = 0x000020, /*< Write a user variable:master or all */ + QUERY_TYPE_USERVAR_READ = 0x000040, /*< Read a user variable:master or any */ + QUERY_TYPE_SYSVAR_READ = 0x000080, /*< Read a system variable:master or any */ + /** Not implemented yet */ +// QUERY_TYPE_SYSVAR_WRITE = 0x000100, /*< Write a system variable:master or all */ + QUERY_TYPE_GSYSVAR_READ = 0x000200, /*< Read global system variable:master or any */ + QUERY_TYPE_GSYSVAR_WRITE = 0x000400, /*< Write global system variable:master or all */ + QUERY_TYPE_BEGIN_TRX = 0x000800, /*< BEGIN or START TRANSACTION */ + QUERY_TYPE_ENABLE_AUTOCOMMIT = 0x001000, /*< SET autocommit=1 */ + QUERY_TYPE_DISABLE_AUTOCOMMIT = 0x002000, /*< SET autocommit=0 */ + QUERY_TYPE_ROLLBACK = 0x004000, /*< ROLLBACK */ + QUERY_TYPE_COMMIT = 0x008000, /*< COMMIT */ + QUERY_TYPE_PREPARE_NAMED_STMT = 0x010000, /*< Prepared stmt with name from user:all */ + QUERY_TYPE_PREPARE_STMT = 0x020000, /*< Prepared stmt with id provided by server:all */ + QUERY_TYPE_EXEC_STMT = 0x040000, /*< Execute prepared statement:master or any */ + QUERY_TYPE_CREATE_TMP_TABLE = 0x080000, /*< Create temporary table:master (could be all) */ + QUERY_TYPE_READ_TMP_TABLE = 0x100000 /*< Read temporary table:master (could be any) */ } skygw_query_type_t; diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 50f90d783..b6e06cdbb 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1118,103 +1118,144 @@ static route_target_t get_route_target ( HINT* hint) { route_target_t target; - - if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT)) - { - /** hints don't affect on routing */ - target = TARGET_ALL; - } - /** - * Read-only statements to slave or to master can be re-routed after - * the hints - */ - else if ((QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || - QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_READ)) && - !trx_active) - { - if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ)) + /** + * These queries are not affected by hints + */ + if (!trx_active && + (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) || + /** Configured to allow writing variables to all nodes */ + (allow_var_writes_to_slaves && + (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE))))) + { + /** hints don't affect on routing */ + target = TARGET_ALL; + } + /** + * Hints may affect on routing of the following queries + */ + else if (!trx_active && + (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || /*< any SELECT */ + QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ)|| /*< read user var */ + QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) || /*< read sys var */ + QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || /*< prepared stmt exec */ + QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ))) /*< read global sys var */ + { + /** First set expected targets before evaluating hints */ + if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) || + /** Configured to allow reading variables from slaves */ + (allow_var_reads_from_slaves && + (QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ)))) { target = TARGET_SLAVE; } - else + else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || + /** Configured not to allow reading variables from slaves */ + (!allow_var_reads_from_slaves && + (QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ)))) { target = TARGET_MASTER; } - /** process routing hints */ - while (hint != NULL) - { - if (hint->type == HINT_ROUTE_TO_MASTER) - { - target = TARGET_MASTER; /*< override */ - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Hint: route to master."))); - break; - } - else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER) - { + + /** process routing hints */ + while (hint != NULL) + { + if (hint->type == HINT_ROUTE_TO_MASTER) + { + target = TARGET_MASTER; /*< override */ + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Hint: route to master."))); + break; + } + else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER) + { /** * Searching for a named server. If it can't be * found, the oroginal target is chosen. */ - target |= TARGET_NAMED_SERVER; + target |= TARGET_NAMED_SERVER; LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, - "Hint: route to named server : "))); + "Hint: route to named server : "))); } - else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER) - { - /** not implemented */ - } - else if (hint->type == HINT_ROUTE_TO_ALL) - { - /** not implemented */ - } - else if (hint->type == HINT_PARAMETER) - { - if (strncasecmp( - (char *)hint->data, - "max_slave_replication_lag", - strlen("max_slave_replication_lag")) == 0) - { - target |= TARGET_RLAG_MAX; - } - else - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Error : Unknown hint parameter " - "'%s' when 'max_slave_replication_lag' " - "was expected.", - (char *)hint->data))); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unknown hint parameter " - "'%s' when 'max_slave_replication_lag' " - "was expected.", - (char *)hint->data))); - } - } - else if (hint->type == HINT_ROUTE_TO_SLAVE) - { + else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER) + { + /** not implemented */ + } + else if (hint->type == HINT_ROUTE_TO_ALL) + { + /** not implemented */ + } + else if (hint->type == HINT_PARAMETER) + { + if (strncasecmp( + (char *)hint->data, + "max_slave_replication_lag", + strlen("max_slave_replication_lag")) == 0) + { + target |= TARGET_RLAG_MAX; + } + else + { + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Error : Unknown hint parameter " + "'%s' when 'max_slave_replication_lag' " + "was expected.", + (char *)hint->data))); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Unknown hint parameter " + "'%s' when 'max_slave_replication_lag' " + "was expected.", + (char *)hint->data))); + } + } + else if (hint->type == HINT_ROUTE_TO_SLAVE) + { target = TARGET_SLAVE; - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Hint: route to slave."))); - } - hint = hint->next; - } /*< while (hint != NULL) */ - } - else - { - /** hints don't affect on routing */ - target = TARGET_MASTER; - } - - return target; + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Hint: route to slave."))); + } + hint = hint->next; + } /*< while (hint != NULL) */ + } + else + { + /** hints don't affect on routing */ + ss_dassert(trx_active || + (QUERY_IS_TYPE(qtype, QUERY_TYPE_WRITE) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_MASTER_READ) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) || + (QUERY_IS_TYPE(qtype, QUERY_TYPE_USERVAR_READ) && + !allow_var_writes_to_slaves) || + (QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ) && + !allow_var_writes_to_slaves) || + (QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_READ) && + !allow_var_writes_to_slaves) || + (QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE) && + !allow_var_writes_to_slaves) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_BEGIN_TRX) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_ROLLBACK) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_COMMIT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_EXEC_STMT) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_CREATE_TMP_TABLE) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_READ_TMP_TABLE) || + QUERY_IS_TYPE(qtype, QUERY_TYPE_UNKNOWN))); + target = TARGET_MASTER; + } + return target; } + /** * Check if the query is a DROP TABLE... query and * if it targets a temporary table, remove it from the hashtable. @@ -1597,11 +1638,9 @@ static int routeQuery( break; } /**< switch by packet type */ - /** * Check if the query has anything to do with temporary tables. */ - qtype = is_read_tmp_table(instance,router_session,querybuf,qtype); check_create_tmp_table(instance,router_session,querybuf,qtype); check_drop_tmp_table(instance,router_session,querybuf,qtype); @@ -1620,7 +1659,7 @@ static int routeQuery( { router_cli_ses->rses_transaction_active = true; } - } + } else if (!router_cli_ses->rses_transaction_active && QUERY_IS_TYPE(qtype, QUERY_TYPE_BEGIN_TRX)) {