From 14b2c149f562be7061373eef04c9a992ff1f7711 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sun, 10 May 2015 06:34:39 +0300 Subject: [PATCH 01/16] Removed resource leaks in regexfilter. --- server/modules/filter/regexfilter.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index ae82cbd70..9c301870b 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -172,7 +172,11 @@ char *logfile = NULL; else if (!strcmp(params[i]->name, "log_trace")) my_instance->log_trace = config_truth_value(params[i]->value); else if (!strcmp(params[i]->name, "log_file")) + { + if(logfile) + free(logfile); logfile = strdup(params[i]->value); + } else if (!filter_standard_parameter(params[i]->name)) { LOGIF(LE, (skygw_log_write_flush( @@ -207,6 +211,7 @@ char *logfile = NULL; if (my_instance->match == NULL || my_instance->replace == NULL) { free(my_instance); + free(logfile); return NULL; } @@ -218,6 +223,7 @@ char *logfile = NULL; free(my_instance->match); free(my_instance->replace); free(my_instance); + free(logfile); return NULL; } From 3ef9b0927d5c0f82ddf392a86954a6afc71ac607 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 11 May 2015 15:33:36 +0300 Subject: [PATCH 02/16] Fixed minor memory leak in mysql_backend when gw_decode_mysql_server_handshake failed. --- server/modules/protocol/mysql_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 2dc77a7ac..0e85f51ed 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -287,7 +287,7 @@ int gw_read_backend_handshake( pthread_self(), conn->owner_dcb->fd, pthread_self()))); - + while(head = gwbuf_consume(head, GWBUF_LENGTH(head))); return 1; } From 8301410c799501ab87c2b829e0be3717ad999b19 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 11 May 2015 22:02:35 +0300 Subject: [PATCH 03/16] Fixed possible memory leaks in schemarouter. --- .../routing/schemarouter/schemarouter.c | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index c31a3b975..cf60bae51 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -1164,9 +1164,9 @@ static void closeSession( dcb_close(router_cli_ses->dcb_reply); dcb_close(router_cli_ses->dcb_route); - if(router_cli_ses->queue) - router_cli_ses->queue = gwbuf_consume( - router_cli_ses->queue,gwbuf_length(router_cli_ses->queue)); + while(router_cli_ses->queue && + (router_cli_ses->queue = gwbuf_consume( + router_cli_ses->queue,gwbuf_length(router_cli_ses->queue)))); /** Unlock */ rses_end_locked_router_action(router_cli_ses); @@ -1195,18 +1195,17 @@ static void freeSession( ROUTER_CLIENT_SES* router_cli_ses; ROUTER_INSTANCE* router; int i; - backend_ref_t* backend_ref; + backend_ref_t* bref; router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session; router = (ROUTER_INSTANCE *)router_instance; - backend_ref = router_cli_ses->rses_backend_ref; for (i=0; irses_nbackends; i++) { - if (!BREF_IS_IN_USE((&backend_ref[i]))) - { - continue; - } + bref = &router_cli_ses->rses_backend_ref[i]; + while(bref->bref_pending_cmd && + (bref->bref_pending_cmd = gwbuf_consume( + bref->bref_pending_cmd,gwbuf_length(bref->bref_pending_cmd)))); } spinlock_acquire(&router->lock); @@ -2237,7 +2236,8 @@ static int routeQuery( */ if (sescmd_cursor_is_active(scur)) { - ss_dassert(bref->bref_pending_cmd == NULL); + ss_dassert((bref->bref_pending_cmd == NULL || + router_cli_ses->rses_closed)); bref->bref_pending_cmd = gwbuf_clone(querybuf); rses_end_locked_router_action(router_cli_ses); From d1a768f6f510b57aab3291482a1dc688f578da60 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 12 May 2015 04:48:25 +0300 Subject: [PATCH 04/16] Updated documentation and created a readwritesplit router document. --- .../Getting-Started/Configuration-Guide.md | 34 ++-- Documentation/routers/ReadWriteSplit.md | 162 ++++++++++++++++++ .../{schemarouter => }/SchemaRouter.md | 0 server/core/maxkeys.c | 2 - server/core/maxpasswd.c | 2 - 5 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 Documentation/routers/ReadWriteSplit.md rename Documentation/routers/{schemarouter => }/SchemaRouter.md (100%) diff --git a/Documentation/Getting-Started/Configuration-Guide.md b/Documentation/Getting-Started/Configuration-Guide.md index 44f85b802..888432fd2 100644 --- a/Documentation/Getting-Started/Configuration-Guide.md +++ b/Documentation/Getting-Started/Configuration-Guide.md @@ -776,23 +776,23 @@ The configuration consists of mandatory and optional parameters. ###### Mandatory parameters -`type` specifies the type of service. For **readwritesplit** module the type is `router`: +**`type`** specifies the type of service. For **readwritesplit** module the type is `router`: type=router -`service` specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: +**`service`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: service=readwritesplit -`servers` provides a list of servers, which must include one master and available slaves: +**`servers`** provides a list of servers, which must include one master and available slaves: servers=server1,server2,server3 **NOTE: Each server on the list must have its own section in the configuration file where it is defined.** -`user` is the username the router session uses for accessing backends in order to load the content of the `mysql.user` table (and `mysql.db` and database names as well) and optionally for creating, and using `maxscale_schema.replication_heartbeat` table. +**`user`** is the username the router session uses for accessing backends in order to load the content of the `mysql.user` table (and `mysql.db` and database names as well) and optionally for creating, and using `maxscale_schema.replication_heartbeat` table. -`passwd` specifies corresponding password for the user. Syntax for user and passwd is: +**`passwd`** specifies corresponding password for the user. Syntax for user and passwd is: ``` user= @@ -801,18 +801,18 @@ passwd= ###### Optional parameters -`max_slave_connections` sets the maximum number of slaves a router session uses at any moment. Default value is `1`. +**`max_slave_connections`** sets the maximum number of slaves a router session uses at any moment. Default value is `1`. max_slave_connections= -`max_slave_replication_lag` specifies how many seconds a slave is allowed to be behind the master. If the lag is bigger than configured value a slave can't be used for routing. +**`max_slave_replication_lag`** specifies how many seconds a slave is allowed to be behind the master. If the lag is bigger than configured value a slave can't be used for routing. max_slave_replication_lag= This applies to Master/Slave replication with MySQL monitor and `detect_replication_lag=1` options set. Please note max_slave_replication_lag must be greater than monitor interval. -`router_options` may include multiple **readwritesplit**-specific options. Values are either singular or parameter-value pairs. Currently available is a single option which specifies the criteria used in slave selection both in initialization of router session and per each query. Note that due to the current monitor implementation, the value specified here should be ** + 1. +**`router_options`** may include multiple **readwritesplit**-specific options. Values are either singular or parameter-value pairs. Currently available is a single option which specifies the criteria used in slave selection both in initialization of router session and per each query. Note that due to the current monitor implementation, the value specified here should be ** + 1. options=slave_selection_criteria= @@ -823,7 +823,7 @@ where ** is one of the following: * `LEAST_BEHIND_MASTER`, the slave with smallest replication lag * `LEAST_CURRENT_OPERATIONS` (default), the slave with least active operations -`use_sql_variables_in` specifies where should queries, which read session variable, be routed. The syntax for `use_sql_variable_in` is: +**`use_sql_variables_in`** specifies where should queries, which read session variable, be routed. The syntax for `use_sql_variable_in` is: use_sql_variables_in=[master|all] @@ -833,13 +833,25 @@ When value all is used, queries reading session variables can be routed to any a In above-mentioned case the user-defined variable would only be updated in the master where query would be routed due to `INSERT` statement. -`max_sescmd_history` sets a limit on how many session commands each session can execute before the connection is closed. The default is an unlimited number of session commands. +**`max_sescmd_history`** sets a limit on how many session commands each session can execute before the connection is closed. The default is an unlimited number of session commands. max_sescmd_history=1500 When a limitation is set, it effectively creates a cap on the session's memory consumption. This might be useful if connection pooling is used and the sessions use large amounts of session commands. -`disable_sescmd_history=true|false` disables the session command history. This way nothing is stored and if a slave server fails and a new one is taken in its stead, the session on that server will be in an inconsistent state compared to the master server. Disabling session command history will allow connection pooling without causing a constant growth in the memory consumption. +**`disable_sescmd_history`** disables the session command history. This way nothing is stored and if a slave server fails and a new one is taken in its stead, the session on that server will be in an inconsistent state compared to the master server. Disabling session command history will allow connection pooling without causing a constant growth in the memory consumption. + +``` +# Disable the session command history +disable_sescmd_history=true +``` + +**`disable_slave_recovery`** disables the recovery and replacement of slave servers. If this option is enabled and a connection to a slave server in use is lost, no replacement slave will be taken. This allows the safe use of session state modifying statements when the session command history is disabled. This is mostly intended to be used with the `disable_sescmd_history` option enabled. + +``` +# Disable the session command history +disable_slave_recovery=true +``` An example of Read/Write Split router configuration : diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md new file mode 100644 index 000000000..2727a4fdf --- /dev/null +++ b/Documentation/routers/ReadWriteSplit.md @@ -0,0 +1,162 @@ +# Readwritesplit + +This document provides a short overview of the **readwritesplit** router module and its intended use case scenarios. It also displays all router configuration parameters with their descriptions. A list of current limitations of the module is included and examples of the router's use are provided. + +## Overview + +The **readwritesplit** router is designed to be used with a Master-Slave replication cluster. It automatically detects changes in the master server and will use the current master server of the cluster. With a Galera cluster, one can achieve a resilient setup and easy master failover by using one of the Galera nodes as a Write-Master node, where are write queries are routed, and spreading the read load over all the nodes. + +## Configuration + +Readwritesplit router-specific settings are specified in the configuration file of MaxScale in its specific section. The section can be freely named but the name is used later as a reference from listener section. + +The configuration consists of mandatory and optional parameters. + +## Mandatory parameters + +**`type`** specifies the type of service. For **readwritesplit** module the type is `router`: + + type=router + +**`service`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: + + service=readwritesplit + +**`servers`** provides a list of servers, which must include one master and available slaves: + + servers=server1,server2,server3 + +**NOTE: Each server on the list must have its own section in the configuration file where it is defined.** + +**`user`** is the username the router session uses for accessing backends in order to load the content of the `mysql.user` table (and `mysql.db` and database names as well) and optionally for creating, and using `maxscale_schema.replication_heartbeat` table. + +**`passwd`** specifies corresponding password for the user. Syntax for user and passwd is: + +``` +user= +passwd= +``` + +## Optional parameters + +**`max_slave_connections`** sets the maximum number of slaves a router session uses at any moment. Default value is `1`. + + max_slave_connections= + +**`max_slave_replication_lag`** specifies how many seconds a slave is allowed to be behind the master. If the lag is bigger than configured value a slave can't be used for routing. + + max_slave_replication_lag= + +This applies to Master/Slave replication with MySQL monitor and `detect_replication_lag=1` options set. +Please note max_slave_replication_lag must be greater than monitor interval. + +**`router_options`** may include multiple **readwritesplit**-specific options. Values are either singular or parameter-value pairs. Currently available is a single option which specifies the criteria used in slave selection both in initialization of router session and per each query. Note that due to the current monitor implementation, the value specified here should be ** + 1. + + options=slave_selection_criteria= + +where ** is one of the following: + +* `LEAST_GLOBAL_CONNECTIONS`, the slave with least connections in total +* `LEAST_ROUTER_CONNECTIONS`, the slave with least connections from this router +* `LEAST_BEHIND_MASTER`, the slave with smallest replication lag +* `LEAST_CURRENT_OPERATIONS` (default), the slave with least active operations + +**`use_sql_variables_in`** specifies where should queries, which read session variable, be routed. The syntax for `use_sql_variable_in` is: + + use_sql_variables_in=[master|all] + +When value all is used, queries reading session variables can be routed to any available slave (depending on selection criteria). Note, that queries modifying session variables are routed to all backend servers by default, excluding write queries with embedded session variable modifications, such as: + + INSERT INTO test.t1 VALUES (@myid:=@myid+1) + +In above-mentioned case the user-defined variable would only be updated in the master where query would be routed due to `INSERT` statement. + +**`max_sescmd_history`** sets a limit on how many session commands each session can execute before the connection is closed. The default is an unlimited number of session commands. + + max_sescmd_history=1500 + +When a limitation is set, it effectively creates a cap on the session's memory consumption. This might be useful if connection pooling is used and the sessions use large amounts of session commands. + +**`disable_sescmd_history`** disables the session command history. This way nothing is stored and if a slave server fails and a new one is taken in its stead, the session on that server will be in an inconsistent state compared to the master server. Disabling session command history will allow connection pooling without causing a constant growth in the memory consumption. + +``` +# Disable the session command history +disable_sescmd_history=true +``` + +**`disable_slave_recovery`** disables the recovery and replacement of slave servers. If this option is enabled and a connection to a slave server in use is lost, no replacement slave will be taken. This allows the safe use of session state modifying statements when the session command history is disabled. This is mostly intended to be used with the `disable_sescmd_history` option enabled. + +``` +# Disable the session command history +disable_slave_recovery=true +``` + +## Limitations + +In Master-Slave replication cluster also read-only queries are routed to master too in the following situations: + +* if they are executed inside an open transaction + +* in case of prepared statement execution + +* statement includes a stored procedure, or an UDF call + +### Limitations in client session handling + +Some of the queries that client sends are routed to all backends instead of sending them just to one of server. These queries include "USE " and “SET autocommit=0” among many others. Readwritesplit sends a copy of these queries to each backend server and forwards the master's reply to the client. Below is a list of MySQL commands which are classified as session commands : +``` +COM_INIT_DB (USE creates this) + +COM_CHANGE_USER + +COM_STMT_CLOSE + +COM_STMT_SEND_LONG_DATA + +COM_STMT_RESET + +COM_STMT_PREPARE + +COM_QUIT (no response, session is closed) + +COM_REFRESH + +COM_DEBUG + +COM_PING + +SQLCOM_CHANGE_DB (USE ... statements) + +SQLCOM_DEALLOCATE_PREPARE + +SQLCOM_PREPARE + +SQLCOM_SET_OPTION + +SELECT ..INTO variable|OUTFILE|DUMPFILE + +SET autocommit=1|0 +``` + +There is a possibility for misbehavior; if `USE mytable` was executed in one of the slaves and it failed, it may be due to replication lag rather than the fact it didn’t exist. Thus the same command may end up with different result among backend servers. This disparity is missed. + +The above-mentioned behavior can be partially controller with the `use_sql_variables_in` configuration parameter. + +``` +use_sql_variables_in=[master|all] (master) +``` + +Server-side session variables are called as SQL variables. If "master" or no value is set, SQL variables are read and written in master only. Autocommit values and prepared statements are routed to all nodes always. + +**NOTE**: If variable is written as a part of write query, it is treated like write query and not routed to all servers. For example, `INSERT INTO test.t1 VALUES (@myvar:= 7)` will be routed to the master and an error in the error log will be written. + +### Examples of limitations + +If new database "db" was created and client executes “USE db” and it is routed to slave before the CREATE DATABASE clause is replicated to all slaves there is a risk of executing query in wrong database. Similarly, if any response that RWSplit sends back to the client differ from that of the master, there is a risk for misbehavior. + +Most imaginable reasons are related to replication lag but it could be possible that a slave fails to execute something because of some non-fatal, temporary failure while execution of same command succeeds in other backends. + + +## Examples + +Examples of the readwritesplit router in use can be found in the [Tutorials](../Tutorials) folder. diff --git a/Documentation/routers/schemarouter/SchemaRouter.md b/Documentation/routers/SchemaRouter.md similarity index 100% rename from Documentation/routers/schemarouter/SchemaRouter.md rename to Documentation/routers/SchemaRouter.md diff --git a/server/core/maxkeys.c b/server/core/maxkeys.c index 9d853e08d..c85dd1d8e 100644 --- a/server/core/maxkeys.c +++ b/server/core/maxkeys.c @@ -69,8 +69,6 @@ int main(int argc, char **argv) arg_vector[5] = "LOGFILE_ERROR"; arg_vector[6] = NULL; skygw_logmanager_init(arg_count,arg_vector); - skygw_log_enable(LOGFILE_TRACE); - skygw_log_enable(LOGFILE_DEBUG); free(arg_vector[2]); free(arg_vector); diff --git a/server/core/maxpasswd.c b/server/core/maxpasswd.c index f456c7b69..453aba38f 100644 --- a/server/core/maxpasswd.c +++ b/server/core/maxpasswd.c @@ -78,8 +78,6 @@ main(int argc, char **argv) arg_vector[5] = "LOGFILE_ERROR"; arg_vector[6] = NULL; skygw_logmanager_init(arg_count,arg_vector); - skygw_log_enable(LOGFILE_TRACE); - skygw_log_enable(LOGFILE_DEBUG); free(arg_vector[2]); free(arg_vector); From 7d3fa8de3725048f37c0668bb65fc142755184c0 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 12 May 2015 04:57:18 +0300 Subject: [PATCH 05/16] Updated readwritesplit documentation. --- Documentation/About/Limitations.md | 34 ++++++++++--------------- Documentation/routers/ReadWriteSplit.md | 7 +++-- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/Documentation/About/Limitations.md b/Documentation/About/Limitations.md index 4bfac725b..01d63008c 100644 --- a/Documentation/About/Limitations.md +++ b/Documentation/About/Limitations.md @@ -37,9 +37,8 @@ In master-slave replication cluster also read-only queries are routed to master * statement includes a stored procedure, or an UDF call ### Limitations in client session handling - -Some of the queries that client sends are routed to all backends instead of sending them just to one of server. These queries include "USE " and “SET autocommit=0” among many others. Read/Write Splitter sends a copy of these queries to each backend server and forwards the first reply it receives to the client. Below is a list of MySQL commands which we call session commands : - +Some of the queries that client sends are routed to all backends instead of sending them just to one of server. These queries include `USE ` and `SET autocommit=0` among many others. Readwritesplit sends a copy of these queries to each backend server and forwards the master's reply to the client. Below is a list of MySQL commands which are classified as session commands : +``` COM_INIT_DB (USE creates this) COM_CHANGE_USER @@ -52,9 +51,7 @@ COM_STMT_RESET COM_STMT_PREPARE -Also these are session commands: - -COM_QUIT (no response) +COM_QUIT (no response, session is closed) COM_REFRESH @@ -62,9 +59,7 @@ COM_DEBUG COM_PING -In addition there are query types which belong to the same group: - -SQLCOM_CHANGE_DB +SQLCOM_CHANGE_DB (USE ... statements) SQLCOM_DEALLOCATE_PREPARE @@ -74,25 +69,24 @@ SQLCOM_SET_OPTION SELECT ..INTO variable|OUTFILE|DUMPFILE -Then there are queries which modify session characteristics, listed as derived, internal RWSplit types: +SET autocommit=1|0 +``` - QUERY_TYPE_ENABLE_AUTOCOMMIT +There is a possibility for misbehavior; if `USE mytable` was executed in one of the slaves and it failed, it may be due to replication lag rather than the fact it didn’t exist. Thus the same command may end up with different result among backend servers. The slaves which fail to execute a session command will be dropped from the active list of slaves for this session to guarantee a consistent session state across all the servers that are in use by the session. - QUERY_TYPE_DISABLE_AUTOCOMMIT +The above-mentioned behavior can be partially controller with the `use_sql_variables_in` configuration parameter. -There is a possibility for misbehavior; if "USE mytable" was executed in one of the slaves and it failed, it may be due to replication lag rather than the fact it didn’t exist. Thus the same command may end up with different result among backend servers. This disparity is missed. - -The above-mentioned behavior can be partially controller with RWSplit configuration parameter called - - use_sql_variables_in=[master|all] (master) +``` +use_sql_variables_in=[master|all] (master) +``` Server-side session variables are called as SQL variables. If "master" or no value is set, SQL variables are read and written in master only. Autocommit values and prepared statements are routed to all nodes always. -NOTE: If variable is written as a part of write query, it is treated like write query and not routed to all servers. For example, INSERT INTO test.t1 VALUES (@myvar:= 7) . +**NOTE**: If variable is written as a part of write query, it is treated like write query and not routed to all servers. For example, `INSERT INTO test.t1 VALUES (@myvar:= 7)` will be routed to the master and an error in the error log will be written. -Examples: +#### Examples of session command limitations -If new database "db" was created and client executes “USE db” and it is routed to slave before the CREATE DATABASE clause is replicated to all slaves there is a risk of executing query in wrong database. Similarly, if any response that RWSplit sends back to the client differ from that of the master, there is a risk for misbehavior. +If new database "db" was created and client executes “USE db” and it is routed to slave before the CREATE DATABASE clause is replicated to all slaves there is a risk of executing query in wrong database. Similarly, if any response that RWSplit sends back to the client differ from that of the master, there is a risk for misbehavior. To prevent this, any failures in session command execution are treated as fatal errors and all connections by the session to that particular slave server will be closed. In addition, the server will not used again for routing for the duration of the session. Most imaginable reasons are related to replication lag but it could be possible that a slave fails to execute something because of some non-fatal, temporary failure while execution of same command succeeds in other backends. diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 2727a4fdf..52b1c11b2 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -103,7 +103,7 @@ In Master-Slave replication cluster also read-only queries are routed to master ### Limitations in client session handling -Some of the queries that client sends are routed to all backends instead of sending them just to one of server. These queries include "USE " and “SET autocommit=0” among many others. Readwritesplit sends a copy of these queries to each backend server and forwards the master's reply to the client. Below is a list of MySQL commands which are classified as session commands : +Some of the queries that client sends are routed to all backends instead of sending them just to one of server. These queries include `USE ` and `SET autocommit=0` among many others. Readwritesplit sends a copy of these queries to each backend server and forwards the master's reply to the client. Below is a list of MySQL commands which are classified as session commands : ``` COM_INIT_DB (USE creates this) @@ -138,7 +138,7 @@ SELECT ..INTO variable|OUTFILE|DUMPFILE SET autocommit=1|0 ``` -There is a possibility for misbehavior; if `USE mytable` was executed in one of the slaves and it failed, it may be due to replication lag rather than the fact it didn’t exist. Thus the same command may end up with different result among backend servers. This disparity is missed. +There is a possibility for misbehavior; if `USE mytable` was executed in one of the slaves and it failed, it may be due to replication lag rather than the fact it didn’t exist. Thus the same command may end up with different result among backend servers. The slaves which fail to execute a session command will be dropped from the active list of slaves for this session to guarantee a consistent session state across all the servers that are in use by the session. The above-mentioned behavior can be partially controller with the `use_sql_variables_in` configuration parameter. @@ -152,11 +152,10 @@ Server-side session variables are called as SQL variables. If "master" or no val ### Examples of limitations -If new database "db" was created and client executes “USE db” and it is routed to slave before the CREATE DATABASE clause is replicated to all slaves there is a risk of executing query in wrong database. Similarly, if any response that RWSplit sends back to the client differ from that of the master, there is a risk for misbehavior. +If new database "db" was created and client executes “USE db” and it is routed to slave before the CREATE DATABASE clause is replicated to all slaves there is a risk of executing query in wrong database. Similarly, if any response that RWSplit sends back to the client differ from that of the master, there is a risk for misbehavior. To prevent this, any failures in session command execution are treated as fatal errors and all connections by the session to that particular slave server will be closed. In addition, the server will not used again for routing for the duration of the session. Most imaginable reasons are related to replication lag but it could be possible that a slave fails to execute something because of some non-fatal, temporary failure while execution of same command succeeds in other backends. - ## Examples Examples of the readwritesplit router in use can be found in the [Tutorials](../Tutorials) folder. From fec1e66a5ca15da6b75be8ca6f062b2a8d9966e9 Mon Sep 17 00:00:00 2001 From: counterpoint Date: Tue, 12 May 2015 11:53:03 +0100 Subject: [PATCH 06/16] Add comment for parameter, correct spelling in comment. --- server/core/dcb.c | 1 + server/include/server.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/core/dcb.c b/server/core/dcb.c index 5c5d0a81d..6717aea41 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -163,6 +163,7 @@ dcb_get_zombies(void) * This routine performs the generic initialisation on the DCB before returning * the newly allocated DCB. * + * @param dcb_role_t The role for the new DCB * @return A newly allocated DCB or NULL if non could be allocated. */ DCB * diff --git a/server/include/server.h b/server/include/server.h index 459869a0b..734f30365 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -118,7 +118,7 @@ typedef struct server { */ #define SERVER_IS_RUNNING(server) (((server)->status & (SERVER_RUNNING|SERVER_MAINT)) == SERVER_RUNNING) /** - * Is the server marked as down - the macro returns true if the server is beleived + * Is the server marked as down - the macro returns true if the server is believed * to be inoperable. */ #define SERVER_IS_DOWN(server) (((server)->status & SERVER_RUNNING) == 0) From 7a8121157753a2e35b083514d7112d58affa510c Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 12 May 2015 14:44:33 +0300 Subject: [PATCH 07/16] Updated Documentation-Contents.md --- Documentation/Documentation-Contents.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 74969c7d1..dfbef26dc 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -42,6 +42,11 @@ - [RabbitMQ Setup and MaxScale Integration Tutorial](Tutorials/RabbitMQ-Setup-And-MaxScale-Integration.md) - [Nagios Plugins for MaxScale Tutorial](Tutorials/Nagios-Plugins.md) +## Routers + + - [Read Write Split](routers/ReadWriteSplit.md) + - [Schemarouter](routers/SchemaRouter.md) + ## Filters Here are detailed documents about the filters MaxScale offers. They contain configuration guides and example use cases. Before reading these,you should have read the filter tutorial so that you know how they work and how to configure them. From 13200e8c74e04be3e363c9a4e642fb8e46b22c00 Mon Sep 17 00:00:00 2001 From: Yuval Hager Date: Tue, 12 May 2015 11:17:30 -0700 Subject: [PATCH 08/16] MXS-137: correct calculation of query length when length is >= 0x80 --- server/core/modutil.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index 60ad6c1b2..ecd3b51a9 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -258,7 +258,8 @@ char * modutil_get_SQL(GWBUF *buf) { unsigned int len, length; -char *ptr, *dptr, *rval = NULL; +unsigned char *ptr; +char *dptr, *rval = NULL; if (!modutil_is_SQL(buf) && !modutil_is_SQL_prepare(buf)) return rval; From efef7def6d7be2de3c9bd6bb6262a9f3d7c1c771 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 13 May 2015 10:55:57 +0300 Subject: [PATCH 09/16] Added test case for MXS-137: https://mariadb.atlassian.net/browse/MXS-137 --- server/core/test/testmodutil.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/server/core/test/testmodutil.c b/server/core/test/testmodutil.c index b37203ff4..f8fe7ef24 100644 --- a/server/core/test/testmodutil.c +++ b/server/core/test/testmodutil.c @@ -66,12 +66,39 @@ int result, length, residual; } +int +test2() +{ +GWBUF *buffer; +char len = 128; +char query[129]; + + buffer = gwbuf_alloc(132); + ss_info_dassert((buffer != NULL),"Buffer should not be null"); + + memset(query,';',128); + memset(query+128,'\0',1); + *((unsigned char*)buffer->start) = len; + *((unsigned char*)buffer->start+1) = 0; + *((unsigned char*)buffer->start+2) = 0; + *((unsigned char*)buffer->start+3) = 1; + *((unsigned char*)buffer->start+4) = 0x03; + memcpy(buffer->start + 5,query,strlen(query)); + char* result = modutil_get_SQL(buffer); + ss_dassert(strcmp(result,query) == 0); + gwbuf_free(buffer); + free(result); + ss_dfprintf(stderr, "\t..done\n"); + return 0; + +} + int main(int argc, char **argv) { int result = 0; result += test1(); - + result += test2(); exit(result); } From 08d978ae4ac243ae0171a833ef1801a2dfc74577 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 13 May 2015 18:30:46 +0300 Subject: [PATCH 10/16] Fixed PROFILING missing from the CMake cache. --- CMakeLists.txt | 9 +++++---- macros.cmake | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19979e1bf..ddc989b7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ endif() IF(DEFINED OLEVEL ) if((OLEVEL GREATER -1) AND (OLEVEL LESS 4) ) - set(FLAGS "${FLAGS} -O${OLEVEL}" CACHE STRING "Compilation flags") + set(FLAGS "${FLAGS} -O${OLEVEL}" CACHE STRING "Compilation flags" FORCE) message(STATUS "Optimization level at: ${OLEVEL}") else() message(WARNING "Optimization level was set to a bad value, ignoring it. (Valid values are 0-3)") @@ -80,16 +80,17 @@ IF(DEFINED OLEVEL ) endif() if(GCOV) - set(FLAGS "${FLAGS} -fprofile-arcs -ftest-coverage" CACHE STRING "Compilation flags") + set(FLAGS "${FLAGS} -fprofile-arcs -ftest-coverage" CACHE STRING "Compilation flags" FORCE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov") endif() if(FAKE_CODE) - set(FLAGS "${FLAGS} -DFAKE_CODE" CACHE STRING "Compilation flags") + set(FLAGS "${FLAGS} -DFAKE_CODE" CACHE STRING "Compilation flags" FORCE) endif() if(PROFILE) - set(FLAGS "${FLAGS} -pg " CACHE STRING "Compilation flags") + message(STATUS "Profiling executables") + set(FLAGS "${FLAGS} -pg " CACHE STRING "Compilation flags" FORCE) endif() set(CMAKE_C_FLAGS "${FLAGS}") diff --git a/macros.cmake b/macros.cmake index 9683a88f6..b7edfed14 100644 --- a/macros.cmake +++ b/macros.cmake @@ -77,6 +77,9 @@ macro(set_variables) # Build extra tools set(BUILD_TOOLS FALSE CACHE BOOL "Build extra utility tools") + # Profiling + set(PROFILE FALSE CACHE BOOL "Profiling (gprof)") + endmacro() macro(check_deps) From 4ed3ffc398d216807e5755c8793186b859888138 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 13 May 2015 19:56:52 +0300 Subject: [PATCH 11/16] Fixed typos in deocumentation --- Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md b/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md index 9ccf7b930..6c1631d4c 100644 --- a/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-1.1.1-Release-Notes.md @@ -56,7 +56,7 @@ There are a number bugs and known limitations within this version of MaxScale, t * Binlog Router Plugin is compatible with MySQL 5.6 Binlog Router Plugin currently does not work for MariaDB 5.5 and MariaDB 10.0 -* LONGBLOG are currently not supported. +* LONGBLOB are currently not supported. * Galera Cluster variables, such as @@wsrep_node_name, are not resolved by the embedded MariaDB parser. @@ -78,14 +78,14 @@ Both RPM and Debian packages are available for MaxScale in addition to the tar b * Ubuntu 12.04 LTS -* Ubuntu 13.10 - * Ubuntu 14.04 LTS * Fedora 19 * Fedora 20 +* Fedora 21 + * OpenSuSE 13 * SuSE Linux Enterprise 11 From 455b132a2271c3bfd445b7713e51f94be7f967c9 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 13 May 2015 22:46:05 +0300 Subject: [PATCH 12/16] Fixed a non-static variable causing multiple declarations when FAKE_CODE was defined --- utils/skygw_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 89f7a4e00..99f7807fc 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -567,7 +567,7 @@ typedef enum skygw_chk_t { #if defined(FAKE_CODE) -bool conn_open[10240]; +static bool conn_open[10240]; #endif /* FAKE_CODE */ #endif /* SKYGW_DEBUG_H */ From 7d3c8673ecad26162e392910c1e85271174b3057 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 14 May 2015 11:02:45 +0200 Subject: [PATCH 13/16] Added unique_name to new server when reloading config Added unique_name to new server when reloading config --- server/core/config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/core/config.c b/server/core/config.c index d22bb777e..44de91b47 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1721,6 +1721,9 @@ SERVER *server; obj->element = server_alloc(address, protocol, atoi(port)); + + server_set_unique_name(obj->element, obj->object); + if (obj->element && monuser && monpw) { serverAddMonUser(obj->element, From 1edf7a32cc004082c47585329d34a165f0d92f7a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 14 May 2015 13:43:23 +0300 Subject: [PATCH 14/16] Added missing strip_db_esc to dbusers.c --- server/core/dbusers.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 8e9d8a715..c8d1a78ec 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -1118,6 +1118,7 @@ getUsers(SERVICE *service, USERS *users) MYSQL_DATABASE_MAXLEN; int dbnames = 0; int db_grants = 0; + char dbnm[MYSQL_DATABASE_MAXLEN+1]; if (serviceGetUser(service, &service_user, &service_passwd) == 0) { @@ -1464,16 +1465,32 @@ getUsers(SERVICE *service, USERS *users) */ if (db_grants) { - /* we have dbgrants, store them */ + bool havedb = false; + /* we have dbgrants, store them */ + if(row[5]){ + unsigned long *rowlen = mysql_fetch_lengths(result); + memcpy(dbnm,row[5],rowlen[5]); + memset(dbnm + rowlen[5],0,1); + havedb = true; + if(service->strip_db_esc) { + strip_escape_chars(dbnm); + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, + "[%s]: %s -> %s", + service->name, + row[5], + dbnm))); + } + } - if(service->optimize_wildcard && wildcard_db_grant(row[5])) + if(service->optimize_wildcard && havedb && wildcard_db_grant(row[5])) { - rc = add_wildcard_users(users, row[0], row[1], password, row[4], row[5], service->resources); + rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources); skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,row[5],rc); } else { - rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], row[5]); + rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, row[4], havedb ? dbnm : NULL); } } else { From 545586b47b436b0a49d0b18341b4bb1bd3bdf939 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 16 May 2015 10:40:42 +0300 Subject: [PATCH 15/16] Added readconnroute documentation. --- Documentation/Documentation-Contents.md | 1 + Documentation/routers/ReadConnRoute.md | 74 +++++++++++++++++++++++++ Documentation/routers/ReadWriteSplit.md | 4 +- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 Documentation/routers/ReadConnRoute.md diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index dfbef26dc..0e553e2dc 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -45,6 +45,7 @@ ## Routers - [Read Write Split](routers/ReadWriteSplit.md) + - [Read Connnection Router](routers/ReadConnRoute.md) - [Schemarouter](routers/SchemaRouter.md) ## Filters diff --git a/Documentation/routers/ReadConnRoute.md b/Documentation/routers/ReadConnRoute.md new file mode 100644 index 000000000..901b72e48 --- /dev/null +++ b/Documentation/routers/ReadConnRoute.md @@ -0,0 +1,74 @@ +# Readconnroute + +This document provides anoverview of the **readconnroute** router module and its intended use case scenarios. It also displays all router configuration parameters with their descriptions. + +## Overview + +The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can be configured to + +## Configuration + +Readconnroute router-specific settings are specified in the configuration file of MaxScale in its specific section. The section can be freely named but the name is used later as a reference from listener section. + +The configuration consists of mandatory and optional parameters. + +## Mandatory parameters + +**`type`** specifies the type of service. For readconnroute module the type is `router`: + + type=router + +**`router`** specifies the router module to be used. For readconnroute the value is `readconnroute`: + + router=readconnroute + +**`servers`** provides a list of servers, which the router will connect to: + + servers=server1,server2,server3 + +**NOTE: Each server on the list must have its own section in the configuration file where it is defined.** + +**`user`** is the username the router session uses for accessing backends in order to load the content of the `mysql.user` table (and `mysql.db` and database names as well) and optionally for creating, and using `maxscale_schema.replication_heartbeat` table. + +**`passwd`** specifies corresponding password for the user. Syntax for user and passwd is: + +``` +user= +passwd= +``` + +## Optional parameters + +**`router_options`** can contain a list of valid server roles. These roles are used as the valid types of servers the router will form connections to when new sessions are created. +``` + router_options=slave +``` +Here is a list of all possible values for the `router_options`. + +Role|Description +------|--------- +master|A server assigned as a master by one of MaxScale monitors. Depending on the monitor implementation, this could be a master server of a Master-Slave replication cluster or a Write-Master of a Galera cluster. +slave|A server assigned as a slave of a master. +synced| A Galera cluster node which is in a synced state with the cluster. +ndb|A MySQL Replication Cluster node +running|A server that is up and running. All servers that MaxScale can connect to are labeled as running. + +If no `router_options` parameter is configured in the service definition, the router will use the default value of `running`. This means that it will load balance connections across all running servers defined in the `servers` parameter of the service. + +## Examples + +The most common use for the readconnroute is to provide either a read or write port for an application. This provides a more lightweight routing solution than the more complex readwritesplit router but requires the application to be able to use distinct write and read ports. + +To configure a read-only service that tolerates master failures, we first need to add a new section in to the configuration file. + +``` +[Read Service] +type=service +router=readconnroute +servers=slave1,slave2,slave3 +router_options=slave +``` + +Here the `router_options`designates slaves as the only valid server type. With this configuration, the queries are load balanced across the slave servers. + +For more complex examples of the readconnroute router, take a look at the examples in the [Tutorials](../Tutorials) folder. diff --git a/Documentation/routers/ReadWriteSplit.md b/Documentation/routers/ReadWriteSplit.md index 52b1c11b2..6058e0390 100644 --- a/Documentation/routers/ReadWriteSplit.md +++ b/Documentation/routers/ReadWriteSplit.md @@ -18,9 +18,9 @@ The configuration consists of mandatory and optional parameters. type=router -**`service`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: +**`router`** specifies the router module to be used. For **readwritesplit** the value is `readwritesplit`: - service=readwritesplit + router=readwritesplit **`servers`** provides a list of servers, which must include one master and available slaves: From 36b963805b7150d9181241c4937a1b3bd67b1355 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 16 May 2015 11:01:30 +0300 Subject: [PATCH 16/16] Added missing weightby parameter into readconnroute documentation. --- Documentation/routers/ReadConnRoute.md | 32 +++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/routers/ReadConnRoute.md b/Documentation/routers/ReadConnRoute.md index 901b72e48..50d5b80ae 100644 --- a/Documentation/routers/ReadConnRoute.md +++ b/Documentation/routers/ReadConnRoute.md @@ -4,7 +4,7 @@ This document provides anoverview of the **readconnroute** router module and its ## Overview -The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can be configured to +The readconnroute router provides simple and lightweight load balancing across a set of servers. The router can also be configured to balance connections based on a weighting parameter defined in the server's section. ## Configuration @@ -39,6 +39,36 @@ passwd= ## Optional parameters +The **`weightby`** parameter defines the name of the value which is used to calculate the weights of the servers. Here is an example server configuration with the `serv_weight` parameter used as the weighting parameter. + +``` +[server1] +type=server +address=127.0.0.1 +port=3000 +protocol=MySQLBackend +serv_weight=3 + +[server2] +type=server +address=127.0.0.1 +port=3001 +protocol=MySQLBackend +serv_weight=1 + +[Read Service] +type=service +router=readconnroute +servers=server1,server2 +weightby=serv_weight +``` + +With this configuration and a heavy query load, the server *server1* will get most of the connections and about a third of the remaining queries are routed to the second server. With server weights, you can assing secondary servers that are only used when the primary server is under heavy load. + +Without the weightby parameter, each connection counts as a single connection. With a weighting parameter, a single connection received its weight from the server's own weighting parameter divided by the sum of all weighting parameters in all the configured servers. + +If we use the previous configuration as an example, the sum of the `serv_weight` parameter is 4. Server1 would receive a weight of `3/4=75%` and server2 would get `1/4=25%`. This means that server1 would get 75% of the connections and server2 would get 25% of the connections. + **`router_options`** can contain a list of valid server roles. These roles are used as the valid types of servers the router will form connections to when new sessions are created. ``` router_options=slave