diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index f1e1f9038..d944a68c3 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -4,7 +4,7 @@ ## About MariaDB MaxScale - [About MariaDB MaxScale](About/About-MaxScale.md) - - [Release Notes](Release-Notes/MaxScale-2.1.3-Release-Notes.md) + - [Release Notes](Release-Notes/MaxScale-2.1.5-Release-Notes.md) - [Changelog](Changelog.md) - [Limitations](About/Limitations.md) diff --git a/Documentation/Release-Notes/MaxScale-2.1.5-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.1.5-Release-Notes.md new file mode 100644 index 000000000..9025c9581 --- /dev/null +++ b/Documentation/Release-Notes/MaxScale-2.1.5-Release-Notes.md @@ -0,0 +1,66 @@ +# MariaDB MaxScale 2.1.5 Release Notes + +Release 2.1.5 is a GA release. + +This document describes the changes in release 2.1.5, when compared to +release [2.1.4](MaxScale-2.1.4-Release-Notes.md). + +If you are upgrading from release 2.0, please also read the following +release notes: +[2.1.4](./MaxScale-2.1.4-Release-Notes.md) +[2.1.3](./MaxScale-2.1.3-Release-Notes.md) +[2.1.2](./MaxScale-2.1.2-Release-Notes.md) +[2.1.1](./MaxScale-2.1.1-Release-Notes.md) +[2.1.0](./MaxScale-2.1.0-Release-Notes.md) + +For any problems you encounter, please consider submitting a bug +report at [Jira](https://jira.mariadb.org). + +## Changed Features + +### Schemarouter + +Starting with MaxScale 2.1.5, the _schemarouter_ will prioritize the current +database over an explicit database if tables in the the current database are +used in a query. + +## New Features + +### Schemarouter + +A new parameter for the _schemarouter_ was added that allows deterministic +resolution of database mapping conflicts (i.e. the database exists on more than +one backend server). + +The new `preferred_server` parameter takes a server name as its value. If a +database mapping conflict occurs, the server given as the parameter will have +preference. In practice, this means that databases on a central server can be +replicated to the shards for doing JOINs but writes to the replicate database +will still go to the central database. + +## Bug fixes + +[Here is a list of bugs fixed since the release of MaxScale 2.1.4.] +(https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.1.5) + +* [MXS-1310](https://jira.mariadb.org/browse/MXS-1310) schemarouter ignores local copy of duplicate schemas on JOIN + +## Known Issues and Limitations + +There are some limitations and known issues within this version of MaxScale. +For more information, please refer to the [Limitations](../About/Limitations.md) document. + +## Packaging + +RPM and Debian packages are provided for the Linux distributions supported +by MariaDB Enterprise. + +Packages can be downloaded [here](https://mariadb.com/resources/downloads). + +## Source Code + +The source code of MaxScale is tagged at GitHub with a tag, which is identical +with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale +is maxscale-X.Y.Z. + +The source code is available [here](https://github.com/mariadb-corporation/MaxScale). diff --git a/Documentation/Routers/SchemaRouter.md b/Documentation/Routers/SchemaRouter.md index d044b8ac6..0e3dc27c2 100644 --- a/Documentation/Routers/SchemaRouter.md +++ b/Documentation/Routers/SchemaRouter.md @@ -51,6 +51,17 @@ List of databases to ignore when checking for duplicate databases. Regular expression that is matched against database names when checking for duplicate databases. +### `preferred_server` + +The name of a server in MaxScale which will be used as the preferred server when +a database is found on more than one server. If a database exists on two +servers, of which neither is the server referred by this parameter, the server +that replies first will be assigned as the location of the database. + +This parameter allows deterministic conflict resolution when a sharded cluster +has a central database server and one or more sharded databases spread across +multiple servers which replicate from the central database server. + **Note:** As of version 2.1 of MaxScale, all of the router options can also be defined as parameters. The values defined in _router_options_ will have priority over the parameters. diff --git a/maxscale-system-test/CMakeLists.txt b/maxscale-system-test/CMakeLists.txt index 29acfa96c..0f5e76971 100644 --- a/maxscale-system-test/CMakeLists.txt +++ b/maxscale-system-test/CMakeLists.txt @@ -299,6 +299,9 @@ add_test_executable(longblob.cpp longblob longblob LABELS readwritesplit readcon # Test with extremely big blob inserting/selecting with > 16 mb data blocks add_test_executable(mxs1110_16mb.cpp mxs1110_16mb longblob_filters LABELS readwritesplit readconnroute HEAVY REPL_BACKEND) +# Schemarouter implicit database detection +add_test_executable(mxs1310_implicit_db.cpp mxs1310_implicit_db mxs1310_implicit_db LABELS schemarouter REPL_BACKEND) + # INSERT extremelly big number of rows add_test_executable(lots_of_rows.cpp lots_of_rows galera LABELS readwritesplit HEAVY GALERA_BACKEND) diff --git a/maxscale-system-test/cnf/maxscale.cnf.template.mxs1310_implicit_db b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1310_implicit_db new file mode 100644 index 000000000..8b1252ed6 --- /dev/null +++ b/maxscale-system-test/cnf/maxscale.cnf.template.mxs1310_implicit_db @@ -0,0 +1,48 @@ +[maxscale] +threads=###threads### +log_info=1 + +[MySQL Monitor] +type=monitor +module=mysqlmon +###repl51### +servers=server1,server2 +user=maxskysql +passwd=skysql + +[Sharding router] +type=service +router=schemarouter +servers=server1,server2 +user=maxskysql +passwd=skysql +auth_all_servers=1 +ignore_databases_regex=.* + +[Sharding Listener] +type=listener +service=Sharding router +protocol=MySQLClient +port=4006 + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +socket=default + +[server1] +type=server +address=###node_server_IP_1### +port=###node_server_port_1### +protocol=MySQLBackend + +[server2] +type=server +address=###node_server_IP_2### +port=###node_server_port_2### +protocol=MySQLBackend diff --git a/maxscale-system-test/mxs1310_implicit_db.cpp b/maxscale-system-test/mxs1310_implicit_db.cpp new file mode 100644 index 000000000..eafc3b7a2 --- /dev/null +++ b/maxscale-system-test/mxs1310_implicit_db.cpp @@ -0,0 +1,54 @@ +/** + * Test for MXS-1310. + * - Only explicit databases used -> shard containing the explicit database + * - Only implicit databases used -> shard containing current database + * - Mix of explicit and implicit databases -> shard containing current database + */ + +#include "testconnections.h" + +int main(int argc, char *argv[]) +{ + TestConnections test(argc, argv); + + // Get the @@server_id value from both shards + char server_id[2][1024]; + test.repl->connect(); + sprintf(server_id[0], "%d", test.repl->get_server_id(0)); + sprintf(server_id[1], "%d", test.repl->get_server_id(1)); + execute_query(test.repl->nodes[0], + "CREATE DATABASE db1;" + "CREATE TABLE db1.t1(id int);" + "INSERT INTO db1.t1 VALUES (@@server_id)"); + execute_query(test.repl->nodes[1], + "CREATE DATABASE db2;" + "CREATE TABLE db2.t2(id int);" + "INSERT INTO db2.t2 VALUES (@@server_id)"); + test.repl->sync_slaves(); + + test.tprintf("Run test with sharded database as active database"); + test.connect_rwsplit(); + test.try_query(test.conn_rwsplit, "USE db2"); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, id FROM t2", server_id[1]); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, id FROM db1.t1", server_id[0]); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, a.id FROM t2 as a JOIN db1.t1 as b", + server_id[1]); + test.close_rwsplit(); + + test.tprintf("Run test with a common database as active database"); + test.connect_rwsplit(); + test.try_query(test.conn_rwsplit, "USE db1"); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, id FROM t1", server_id[0]); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, id FROM db2.t2", server_id[1]); + execute_query_check_one(test.conn_rwsplit, "SELECT @@server_id, a.id FROM t1 as a JOIN db1.t1 as b", + server_id[0]); + test.close_rwsplit(); + + // Cleanup + execute_query(test.repl->nodes[0], "DROP DATABASE db1"); + execute_query(test.repl->nodes[1], "DROP DATABASE db2"); + + test.repl->fix_replication(); + + return test.global_result; +} diff --git a/server/modules/routing/schemarouter/schemarouter.hh b/server/modules/routing/schemarouter/schemarouter.hh index aa5ff84c9..95830ce66 100644 --- a/server/modules/routing/schemarouter/schemarouter.hh +++ b/server/modules/routing/schemarouter/schemarouter.hh @@ -46,13 +46,15 @@ struct Config pcre2_code* ignore_regex; /**< Regular expression used to ignore databases */ pcre2_match_data* ignore_match_data; /**< Match data for @c ignore_regex */ std::set ignored_dbs; /**< Set of ignored databases */ + SERVER* preferred_server; /**< Server to prefer in conflict situations */ Config(): refresh_min_interval(0.0), refresh_databases(false), debug(false), ignore_regex(NULL), - ignore_match_data(NULL) + ignore_match_data(NULL), + preferred_server(NULL) { } }; diff --git a/server/modules/routing/schemarouter/schemarouterinstance.cc b/server/modules/routing/schemarouter/schemarouterinstance.cc index 640c1f667..329a14108 100644 --- a/server/modules/routing/schemarouter/schemarouterinstance.cc +++ b/server/modules/routing/schemarouter/schemarouterinstance.cc @@ -75,6 +75,7 @@ SchemaRouter* SchemaRouter::create(SERVICE* pService, char** pzOptions) config.refresh_databases = config_get_bool(conf, "refresh_databases"); config.refresh_min_interval = config_get_integer(conf, "refresh_interval"); config.debug = config_get_bool(conf, "debug"); + config.preferred_server = config_get_server(conf, "preferred_server"); /** Add default system databases to ignore */ config.ignored_dbs.insert("mysql"); @@ -401,6 +402,7 @@ MXS_MODULE* MXS_CREATE_MODULE() {"refresh_databases", MXS_MODULE_PARAM_BOOL, "true"}, {"refresh_interval", MXS_MODULE_PARAM_COUNT, DEFAULT_REFRESH_INTERVAL}, {"debug", MXS_MODULE_PARAM_BOOL, "false"}, + {"preferred_server", MXS_MODULE_PARAM_SERVER}, {MXS_END_MODULE_PARAMS} } }; diff --git a/server/modules/routing/schemarouter/schemaroutersession.cc b/server/modules/routing/schemarouter/schemaroutersession.cc index cf904b181..174f52b36 100644 --- a/server/modules/routing/schemarouter/schemaroutersession.cc +++ b/server/modules/routing/schemarouter/schemaroutersession.cc @@ -1322,6 +1322,14 @@ enum showdb_response SchemaRouterSession::parse_mapping_response(SSRBackend& bre data, target->unique_name, duplicate->unique_name, m_client->user, m_client->remote); } + else if (m_config->preferred_server == target) + { + /** In conflict situations, use the preferred server */ + MXS_INFO("Forcing location of '%s' from '%s' to '%s'", + data, m_shard.get_location(data)->unique_name, + target->unique_name); + m_shard.replace_location(data, target); + } } MXS_FREE(data); } diff --git a/server/modules/routing/schemarouter/shard_map.cc b/server/modules/routing/schemarouter/shard_map.cc index f26acd6b8..f2981c290 100644 --- a/server/modules/routing/schemarouter/shard_map.cc +++ b/server/modules/routing/schemarouter/shard_map.cc @@ -32,6 +32,12 @@ bool Shard::add_location(std::string db, SERVER* target) return m_map.insert(std::make_pair(db, target)).second; } +void Shard::replace_location(std::string db, SERVER* target) +{ + std::transform(db.begin(), db.end(), db.begin(), ::tolower); + m_map[db] = target; +} + SERVER* Shard::get_location(std::string db) { SERVER* rval = NULL; diff --git a/server/modules/routing/schemarouter/shard_map.hh b/server/modules/routing/schemarouter/shard_map.hh index 90f69183b..acdad52bc 100644 --- a/server/modules/routing/schemarouter/shard_map.hh +++ b/server/modules/routing/schemarouter/shard_map.hh @@ -52,6 +52,14 @@ public: */ SERVER* get_location(std::string db); + /** + * @brief Change the location of a database + * + * @param db Database to relocate + * @param target Target where database is relocated to + */ + void replace_location(std::string db, SERVER* target); + /** * @brief Check if shard contains stale information *