Merge branch '2.2' into develop
This commit is contained in:
commit
5326c8db5c
@ -55,6 +55,7 @@ include_directories(BEFORE ${PCRE2_INCLUDE_DIRS})
|
||||
if(NOT MARIADB_CONNECTOR_FOUND)
|
||||
message(STATUS "Building MariaDB Connector-C from source.")
|
||||
include(cmake/BuildMariaDBConnector.cmake)
|
||||
include_directories(BEFORE ${MARIADB_CONNECTOR_INCLUDE_DIR})
|
||||
else()
|
||||
# This is required as the core depends on the `connector-c` target
|
||||
add_custom_target(connector-c)
|
||||
|
@ -366,18 +366,19 @@ privilege.
|
||||
parameters. If password encryption is in use, `replication_password` must be
|
||||
encrypted with the same key to avoid erroneous decryption.
|
||||
|
||||
#### `failover_timeout`
|
||||
#### `failover_timeout` and `switchover_timeout`
|
||||
|
||||
Time limit for the cluster failover in seconds. The default value is 90
|
||||
seconds.
|
||||
Time limit for the cluster failover and switchover in seconds. The default values
|
||||
are 90 seconds.
|
||||
|
||||
If no successful failover takes place within the configured time period, a
|
||||
message is logged and automatic failover is disabled.
|
||||
If no successful failover/switchover takes place within the configured time
|
||||
period, a message is logged and automatic failover is disabled. This prevents
|
||||
further automatic modifications to the misbehaving cluster.
|
||||
|
||||
This parameter also controls how long a MaxScale instance that has transitioned
|
||||
from passive to active will wait for a failover to take place after an apparent
|
||||
loss of a master server. If no new master server is detected within the
|
||||
configured time period, failover will be initiated again.
|
||||
`failover_timeout` also controls how long a MaxScale instance that has
|
||||
transitioned from passive to active will wait for a failover to take place after
|
||||
an apparent loss of a master server. If no new master server is detected within
|
||||
the configured time period, failover will be initiated again.
|
||||
|
||||
#### `verify_master_failure` and `master_failure_timeout`
|
||||
|
||||
@ -398,15 +399,17 @@ disconnection.
|
||||
For automatic failover to activate, the `failcount` requirement must also be
|
||||
met.
|
||||
|
||||
#### `switchover_timeout`
|
||||
#### `servers_no_promotion`
|
||||
|
||||
Time limit for cluster switchover in seconds. The default value is 90
|
||||
seconds.
|
||||
This is a comma-separated list of server names that will not be chosen for
|
||||
master promotion during a failover. This does not affect switchover since in
|
||||
that case the user selects the server. Using this list can disrupt new master
|
||||
selection such that an unoptimal server is chosen. At worst, this will cause
|
||||
replication to break.
|
||||
|
||||
If no successful switchover takes place within the configured time period, a
|
||||
message is logged and automatic failover is disabled, even if it was enabled
|
||||
before the switchover attempt. This prevents further modifications to the
|
||||
misbehaving cluster.
|
||||
```
|
||||
servers_no_promotion=backup_dc_server1,backup_dc_server2
|
||||
```
|
||||
|
||||
### Manual switchover and failover
|
||||
|
||||
|
@ -590,7 +590,8 @@ of parameters can be altered at runtime:
|
||||
"auth_read_timeout",
|
||||
"auth_write_timeout",
|
||||
"admin_auth",
|
||||
"admin_log_auth_failures"
|
||||
"admin_log_auth_failures",
|
||||
"passive"
|
||||
]
|
||||
|
||||
## rotate
|
||||
|
@ -213,22 +213,25 @@ transaction_safety=on to enable detection of incomplete transactions.
|
||||
### `send_slave_heartbeat`
|
||||
|
||||
This defines whether MariaDB MaxScale sends the heartbeat packet to the slave
|
||||
when there are no real binlog events to send. The default value is 'off' and no
|
||||
heartbeat events are sent to slave servers.
|
||||
when there are no real binlog events to send. This parameter takes a boolean
|
||||
value and the default value is false. This means that no heartbeat events are
|
||||
sent to slave servers.
|
||||
|
||||
If value is 'on' the interval value (requested by the slave during registration)
|
||||
is reported in the diagnostic output and the packet is send after the time
|
||||
interval without any event to send.
|
||||
If value is set to true the interval value (requested by the slave during
|
||||
registration) is reported in the diagnostic output and the packet is send after
|
||||
the time interval without any event to send.
|
||||
|
||||
### `semisync`
|
||||
|
||||
This parameter controls whether binlog server could ask Master server to start
|
||||
the Semi-Synchronous replication. In order to get semi-sync working, the Master
|
||||
server must have the *rpl_semi_sync_master* plugin installed. The availability
|
||||
of the plugin and the value of the GLOBAL VARIABLE
|
||||
*rpl_semi_sync_master_enabled* are checked in the Master registration phase: if
|
||||
the plugin is installed in the Master database, the binlog server subsequently
|
||||
requests the semi-sync option.
|
||||
the Semi-Synchronous replication. This parameter takes a boolean value and the
|
||||
default value is false.
|
||||
|
||||
In order to get semi-sync working, the Master server must have the
|
||||
*rpl_semi_sync_master* plugin installed. The availability of the plugin and the
|
||||
value of the GLOBAL VARIABLE *rpl_semi_sync_master_enabled* are checked in the
|
||||
Master registration phase: if the plugin is installed in the Master database,
|
||||
the binlog server subsequently requests the semi-sync option.
|
||||
|
||||
Note:
|
||||
- the network replication stream from Master has two additional bytes before
|
||||
|
@ -214,7 +214,7 @@ typedef struct mxs_filter_object
|
||||
* is changed these values must be updated in line with the rules in the
|
||||
* file modinfo.h.
|
||||
*/
|
||||
#define MXS_FILTER_VERSION {2, 2, 0}
|
||||
#define MXS_FILTER_VERSION {3, 0, 0}
|
||||
|
||||
/**
|
||||
* MXS_FILTER_DEF represents a filter definition from the configuration file.
|
||||
|
@ -18,8 +18,8 @@
|
||||
* @see http://jsonapi.org/format/
|
||||
*/
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
#include <maxscale/jansson.hh>
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <maxscale/jansson.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
|
@ -18,13 +18,13 @@
|
||||
|
||||
#include <maxscale/cdefs.h>
|
||||
|
||||
#include <mysql.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <maxscale/config.h>
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/server.h>
|
||||
#include <maxscale/jansson.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
@ -95,7 +95,7 @@ typedef struct mxs_monitor_object
|
||||
* The monitor API version number. Any change to the monitor module API
|
||||
* must change these versions using the rules defined in modinfo.h
|
||||
*/
|
||||
#define MXS_MONITOR_VERSION {3, 0, 0}
|
||||
#define MXS_MONITOR_VERSION {3, 1, 0}
|
||||
|
||||
/**
|
||||
* Specifies capabilities specific for monitor.
|
||||
@ -382,6 +382,19 @@ void load_server_journal(MXS_MONITOR *monitor, MXS_MONITORED_SERVER **master);
|
||||
* @param search_server Server to search for
|
||||
* @return Found monitored server or NULL if not found
|
||||
*/
|
||||
MXS_MONITORED_SERVER* mon_get_monitored_server(MXS_MONITOR* mon, SERVER* search_server);
|
||||
MXS_MONITORED_SERVER* mon_get_monitored_server(const MXS_MONITOR* mon, SERVER* search_server);
|
||||
|
||||
/**
|
||||
* Get an array of monitored servers. All the servers defined in the config setting must be monitored by
|
||||
* the given monitor.
|
||||
*
|
||||
* @param params Config parameters
|
||||
* @param key Setting name
|
||||
* @param mon Monitor which should monitor the servers
|
||||
* @param monitored_servers_out Where to save output. The caller should free the array, but not the elements.
|
||||
* @return Output array size if successful, negative value otherwise
|
||||
*/
|
||||
int mon_config_get_servers(const MXS_CONFIG_PARAMETER* params, const char* key, const MXS_MONITOR* mon,
|
||||
MXS_MONITORED_SERVER*** monitored_array_out);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <maxscale/cdefs.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <mysql.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <maxscale/server.h>
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
@ -124,4 +124,19 @@ mxs_mysql_name_kind_t mxs_mysql_name_to_pcre(char *pcre,
|
||||
*/
|
||||
void mxs_mysql_set_server_version(MYSQL* mysql, SERVER* server);
|
||||
|
||||
/**
|
||||
* Enable/disable the logging of all SQL statements MaxScale sends to
|
||||
* the servers.
|
||||
*
|
||||
* @param enable If true, enable, if false, disable.
|
||||
*/
|
||||
void mxs_mysql_set_log_statements(bool enable);
|
||||
|
||||
/**
|
||||
* Returns whether SQL statements sent to the servers are logged or not.
|
||||
*
|
||||
* @return True, if statements are logged, false otherwise.
|
||||
*/
|
||||
bool mxs_mysql_get_log_statements();
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
#define QUERY_CLASSIFIER_VERSION {1, 1, 0}
|
||||
#define MXS_QUERY_CLASSIFIER_VERSION {2, 0, 0}
|
||||
|
||||
/**
|
||||
* qc_init_kind_t specifies what kind of initialization should be performed.
|
||||
|
@ -218,7 +218,7 @@ typedef struct mxs_router_object
|
||||
* must update these versions numbers in accordance with the rules in
|
||||
* modinfo.h.
|
||||
*/
|
||||
#define MXS_ROUTER_VERSION { 2, 0, 0 }
|
||||
#define MXS_ROUTER_VERSION { 3, 0, 0 }
|
||||
|
||||
/**
|
||||
* Specifies capabilities specific for routers. Common capabilities
|
||||
|
@ -1,5 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install dependencies
|
||||
npm i
|
||||
|
||||
ITEMS=`node maxctrl.js help|awk '/^$/{p=0} {if(p){print $1}}/Commands:/{p=1}'`
|
||||
|
||||
TOC=$(for i in $ITEMS
|
||||
|
@ -35,7 +35,8 @@ const maxscale_params = [
|
||||
'auth_read_timeout',
|
||||
'auth_write_timeout',
|
||||
'admin_auth',
|
||||
'admin_log_auth_failures'
|
||||
'admin_log_auth_failures',
|
||||
'passive'
|
||||
]
|
||||
|
||||
exports.command = 'alter <command>'
|
||||
|
@ -308,6 +308,9 @@ add_test_executable(mysqlmon_failover_stress.cpp mysqlmon_failover_stress mysqlm
|
||||
# MySQL Monitor switchover stress
|
||||
add_test_executable(mysqlmon_switchover_stress.cpp mysqlmon_switchover_stress mysqlmon_switchover_stress LABELS mysqlmon REPL_BACKEND)
|
||||
|
||||
# Check monitoring and failover when ignore_external_masters is enabled
|
||||
add_test_executable(mysqlmon_external_master.cpp mysqlmon_external_master mysqlmon_external_master LABELS mysqlmon REPL_BACKEND)
|
||||
|
||||
# Test monitor state change events when manually clearing server bits
|
||||
add_test_executable(false_monitor_state_change.cpp false_monitor_state_change replication LABELS mysqlmon REPL_BACKEND)
|
||||
|
||||
@ -594,6 +597,10 @@ add_test_executable(mxs1543.cpp mxs1543 avro LABELS REPL_BACKEND)
|
||||
# https://jira.mariadb.org/browse/MXS-1585
|
||||
add_test_executable(mxs1585.cpp mxs1585 mxs1585 LABELS REPL_BACKEND)
|
||||
|
||||
# MXS-1643: Too many monitor events are triggered
|
||||
# https://jira.mariadb.org/browse/MXS-1643
|
||||
add_test_executable(mxs1643_extra_events.cpp mxs1643_extra_events mxs1643_extra_events LABELS REPL_BACKEND)
|
||||
|
||||
# 'namedserverfilter' test
|
||||
add_test_executable(namedserverfilter.cpp namedserverfilter namedserverfilter LABELS namedserverfilter LIGHT REPL_BACKEND)
|
||||
|
||||
|
@ -31,6 +31,43 @@ int failed_transaction_num = 0;
|
||||
/** The amount of rows each transaction inserts */
|
||||
const int N_INSERTS = 100;
|
||||
|
||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
bool sync_servers(MYSQL* master, MYSQL* slave)
|
||||
{
|
||||
bool rval = false;
|
||||
int t = 240;
|
||||
|
||||
for (int i = 0; i < t; i++)
|
||||
{
|
||||
char master_log_file[80] = "";
|
||||
char master_log_pos[80] = "";
|
||||
char slave_log_file[80] = "";
|
||||
char slave_log_pos[80] = "";
|
||||
find_field(master, "SHOW MASTER STATUS", "File", master_log_file);
|
||||
find_field(master, "SHOW MASTER STATUS", "Position", master_log_pos);
|
||||
find_field(slave, "SHOW SLAVE STATUS", "Master_Log_File", slave_log_file);
|
||||
find_field(slave, "SHOW SLAVE STATUS", "Read_Master_Log_Pos", slave_log_pos);
|
||||
|
||||
if (strcmp(slave_log_file, master_log_file) == 0 && strcmp(slave_log_pos, master_log_pos) == 0)
|
||||
{
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!rval)
|
||||
{
|
||||
printf("WARNING: Slave has not caught up in %d seconds. Test will most likely fail.\n", t);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int transaction(MYSQL * conn, int N)
|
||||
{
|
||||
int local_result = 0;
|
||||
@ -83,6 +120,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
Test->repl->connect();
|
||||
execute_query(Test->repl->nodes[0], (char *) "DROP TABLE IF EXISTS t1;");
|
||||
Test->repl->sync_slaves();
|
||||
Test->repl->close_connections();
|
||||
sleep(5);
|
||||
|
||||
@ -91,8 +129,6 @@ int main(int argc, char *argv[])
|
||||
Test->repl->execute_query_all_nodes((char *) "RESET SLAVE ALL");
|
||||
Test->repl->execute_query_all_nodes((char *) "RESET MASTER");
|
||||
|
||||
Test->repl->verbose = true;
|
||||
|
||||
Test->tprintf("Starting binlog configuration\n");
|
||||
Test->start_binlog(0);
|
||||
|
||||
@ -120,11 +156,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
sleep(15);
|
||||
|
||||
Test->tprintf("Blocking master\n");
|
||||
Test->repl->block_node(0);
|
||||
Test->stop_timeout();
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
sleep(180);
|
||||
sync_servers(Test->repl->nodes[0], Test->repl->nodes[3]);
|
||||
Test->tprintf("Blocking master");
|
||||
Test->repl->block_node(0);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
for (int i = 0; i < 180 && exit_flag == 0; i++)
|
||||
{
|
||||
sleep(i);
|
||||
}
|
||||
|
||||
Test->tprintf("Done! Waiting for thread\n");
|
||||
exit_flag = 1;
|
||||
@ -135,10 +178,8 @@ int main(int argc, char *argv[])
|
||||
char rep[256];
|
||||
int rep_d;
|
||||
|
||||
Test->tprintf("Sleeping to let replication happen\n");
|
||||
sleep(30);
|
||||
|
||||
Test->repl->connect();
|
||||
sync_servers(Test->repl->nodes[2], Test->repl->nodes[3]);
|
||||
|
||||
for (int i_n = 3; i_n < Test->repl->N; i_n++)
|
||||
{
|
||||
@ -321,6 +362,8 @@ void *transaction_thread( void *ptr )
|
||||
|
||||
while ((exit_flag == 0) && i_trans < trans_max)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
trans_result = transaction(conn, i_trans);
|
||||
if (trans_result != 0)
|
||||
{
|
||||
@ -328,7 +371,7 @@ void *transaction_thread( void *ptr )
|
||||
failed_transaction_num = i_trans;
|
||||
Test->tprintf("Closing connection\n");
|
||||
mysql_close(conn);
|
||||
Test->tprintf("Waiting for repication\n");
|
||||
Test->tprintf("Waiting for replication");
|
||||
sleep(15);
|
||||
Test->tprintf("Calling select_new_master()\n");
|
||||
select_new_master(Test);
|
||||
@ -341,8 +384,11 @@ void *transaction_thread( void *ptr )
|
||||
i_trans--;
|
||||
}
|
||||
i_trans++;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
i_trans--;
|
||||
|
||||
exit_flag = 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
[maxscale]
|
||||
threads=###threads###
|
||||
|
||||
[MySQL-Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
servers= server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd= skysql
|
||||
monitor_interval=1000
|
||||
detect_standalone_master=true
|
||||
failcount=1
|
||||
allow_cluster_recovery=true
|
||||
replication_user=repl
|
||||
replication_password=repl
|
||||
backend_connect_timeout=3
|
||||
backend_read_timeout=3
|
||||
backend_write_timeout=3
|
||||
auto_failover=true
|
||||
auto_rejoin=true
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router= readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split 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
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=###node_server_IP_3###
|
||||
port=###node_server_port_3###
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server4]
|
||||
type=server
|
||||
address=###node_server_IP_4###
|
||||
port=###node_server_port_4###
|
||||
protocol=MySQLBackend
|
@ -0,0 +1,59 @@
|
||||
[maxscale]
|
||||
threads=###threads###
|
||||
|
||||
[MySQL-Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
servers=server1,server2,server3
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
monitor_interval=1000
|
||||
detect_standalone_master=true
|
||||
failcount=1
|
||||
allow_cluster_recovery=true
|
||||
auto_failover=true
|
||||
auto_rejoin=true
|
||||
replication_user=repl
|
||||
replication_password=repl
|
||||
ignore_external_masters=true
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split 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
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=###node_server_IP_3###
|
||||
port=###node_server_port_3###
|
||||
protocol=MySQLBackend
|
@ -8,18 +8,7 @@ using std::endl;
|
||||
|
||||
void replicate_from(TestConnections& test, int server_ind, int target_ind)
|
||||
{
|
||||
stringstream change_master;
|
||||
change_master << "CHANGE MASTER TO MASTER_HOST = '" << test.repl->IP[target_ind]
|
||||
<< "', MASTER_PORT = " << test.repl->port[target_ind] << ", MASTER_USE_GTID = current_pos, "
|
||||
"MASTER_USER='repl', MASTER_PASSWORD='repl';";
|
||||
cout << "Server " << server_ind + 1 << " starting to replicate from server " << target_ind + 1 << endl;
|
||||
if (test.verbose)
|
||||
{
|
||||
cout << "Query is '" << change_master.str() << "'" << endl;
|
||||
}
|
||||
execute_query(test.repl->nodes[server_ind], "STOP SLAVE;");
|
||||
execute_query(test.repl->nodes[server_ind], change_master.str().c_str());
|
||||
execute_query(test.repl->nodes[server_ind], "START SLAVE;");
|
||||
test.repl->replicate_from(server_ind, target_ind);
|
||||
}
|
||||
|
||||
void reset_replication(TestConnections& test)
|
||||
|
@ -79,7 +79,13 @@ void get_maxscale_ips(TestConnections& test, vector<string>* pIps)
|
||||
to_collection(output, "\n", pIps);
|
||||
transform(pIps->begin(), pIps->end(), pIps->begin(), extract_ip);
|
||||
|
||||
pIps->erase(find(pIps->begin(), pIps->end(), "127.0.0.1"));
|
||||
// Remove 127.0.0.1 if it is present.
|
||||
auto i = find(pIps->begin(), pIps->end(), "127.0.0.1");
|
||||
|
||||
if (i != pIps->end())
|
||||
{
|
||||
pIps->erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
@ -385,6 +386,7 @@ int Mariadb_nodes::start_replication()
|
||||
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
execute_query(nodes[i], "SET GLOBAL read_only=OFF");
|
||||
execute_query(nodes[i], "STOP SLAVE;");
|
||||
|
||||
if (g_require_gtid)
|
||||
@ -551,6 +553,20 @@ int Mariadb_nodes::unblock_all_nodes()
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool is_readonly(MYSQL* conn)
|
||||
{
|
||||
bool rval = false;
|
||||
char output[512];
|
||||
find_field(conn, "SHOW VARIABLES LIKE 'read_only'", "Value", output);
|
||||
|
||||
if (strcasecmp(output, "OFF") != 0)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool Mariadb_nodes::check_master_node(MYSQL *conn)
|
||||
{
|
||||
bool rval = true;
|
||||
@ -600,10 +616,7 @@ bool Mariadb_nodes::check_master_node(MYSQL *conn)
|
||||
}
|
||||
}
|
||||
|
||||
char output[512];
|
||||
find_field(conn, "SHOW VARIABLES LIKE 'read_only'", "Value", output);
|
||||
|
||||
if (strcmp(output, "OFF"))
|
||||
if (is_readonly(conn))
|
||||
{
|
||||
printf("The master is in read-only mode\n");
|
||||
rval = false;
|
||||
@ -754,7 +767,8 @@ int Mariadb_nodes::check_replication()
|
||||
else if (bad_slave_thread_status(nodes[i], "Slave_IO_Running", i) ||
|
||||
bad_slave_thread_status(nodes[i], "Slave_SQL_Running", i) ||
|
||||
wrong_replication_type(nodes[i]) ||
|
||||
multi_source_replication(nodes[i], i))
|
||||
multi_source_replication(nodes[i], i) ||
|
||||
is_readonly(nodes[i]))
|
||||
{
|
||||
res = 1;
|
||||
if (verbose)
|
||||
@ -1444,3 +1458,21 @@ int Mariadb_nodes::prepare_servers()
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Mariadb_nodes::replicate_from(int slave, int master, const char* type)
|
||||
{
|
||||
std::stringstream change_master;
|
||||
change_master << "CHANGE MASTER TO MASTER_HOST = '" << IP[master]
|
||||
<< "', MASTER_PORT = " << port[master] << ", MASTER_USE_GTID = " << type << ", "
|
||||
"MASTER_USER='repl', MASTER_PASSWORD='repl';";
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cout << "Server " << slave + 1 << " starting to replicate from server " << master + 1 << std::endl;
|
||||
std::cout << "Query is '" << change_master.str() << "'" << std::endl;
|
||||
}
|
||||
|
||||
execute_query(nodes[slave], "STOP SLAVE;");
|
||||
execute_query(nodes[slave], change_master.str().c_str());
|
||||
execute_query(nodes[slave], "START SLAVE;");
|
||||
}
|
||||
|
@ -430,6 +430,18 @@ public:
|
||||
/** Whether to require GTID based replication, defaults to false */
|
||||
static void require_gtid(bool value);
|
||||
|
||||
/**
|
||||
* Configure a server as a slave of another server
|
||||
*
|
||||
* The servers are configured with GTID replicating using the configured
|
||||
* GTID position, either slave_pos or current_pos.
|
||||
*
|
||||
* @param slave The node index to assign as slave
|
||||
* @param master The node index of the master
|
||||
* @param type Replication type
|
||||
*/
|
||||
void replicate_from(int slave, int master, const char* type = "current_pos");
|
||||
|
||||
private:
|
||||
|
||||
bool check_master_node(MYSQL *conn);
|
||||
|
@ -25,28 +25,6 @@ void change_master(TestConnections& test, int slave, int master, const char* nam
|
||||
source.c_str(), test.repl->IP[master], test.repl->user_name, test.repl->password, source.c_str());
|
||||
}
|
||||
|
||||
std::string dump_status(const StringSet& current, const StringSet& expected)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Current status: (";
|
||||
|
||||
for (const auto& a: current)
|
||||
{
|
||||
ss << a << ",";
|
||||
}
|
||||
|
||||
ss << ") Expected status: (";
|
||||
|
||||
for (const auto& a: expected)
|
||||
{
|
||||
ss << a << ",";
|
||||
}
|
||||
|
||||
ss << ")";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void check_status(TestConnections& test, const StringSet& expected_master, const StringSet& expected_slave)
|
||||
{
|
||||
sleep(2);
|
||||
|
39
maxscale-system-test/mxs1643_extra_events.cpp
Normal file
39
maxscale-system-test/mxs1643_extra_events.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* MXS-1643: Too many monitor events are triggered
|
||||
*
|
||||
* https://jira.mariadb.org/browse/MXS-1643
|
||||
*/
|
||||
#include "testconnections.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Mariadb_nodes::require_gtid(true);
|
||||
TestConnections test(argc, argv);
|
||||
|
||||
// Check that master gets the slave status when set into read-only mode
|
||||
test.tprintf("Set master into read-only mode");
|
||||
test.repl->connect();
|
||||
execute_query(test.repl->nodes[0], "SET GLOBAL read_only=ON");
|
||||
sleep(10);
|
||||
test.tprintf("Check that the current master now has the slave label");
|
||||
test.check_log_err(0, "[Master, Running] -> [Running]", false);
|
||||
test.check_log_err(0, "[Master, Running] -> [Slave, Running]", true);
|
||||
execute_query(test.repl->nodes[0], "SET GLOBAL read_only=OFF");
|
||||
sleep(5);
|
||||
test.maxscales->ssh_node_f(0, true, "truncate -s 0 /var/log/maxscale/maxscale.log");
|
||||
|
||||
// Check that the Master and Slave status aren't both set
|
||||
test.tprintf("Block master and wait for monitor to detect it.");
|
||||
test.repl->block_node(0);
|
||||
sleep(10);
|
||||
test.tprintf("Check that the new master doesn't have both slave and master labels");
|
||||
test.check_log_err(0, "[Slave, Running] -> [Master, Slave, Running]", false);
|
||||
test.check_log_err(0, "[Slave, Running] -> [Master, Running]", true);
|
||||
test.repl->unblock_node(0);
|
||||
|
||||
|
||||
test.tprintf("Cleanup");
|
||||
test.repl->execute_query_all_nodes( "STOP ALL SLAVES; RESET SLAVE ALL;");
|
||||
test.repl->fix_replication();
|
||||
return test.global_result;
|
||||
}
|
114
maxscale-system-test/mysqlmon_external_master.cpp
Normal file
114
maxscale-system-test/mysqlmon_external_master.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Test monitoring and failover with ignore_external_masters=true
|
||||
*/
|
||||
#include "testconnections.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#define DOWN "Down"
|
||||
#define RUNNING "Running"
|
||||
#define MASTER "Master"
|
||||
#define SLAVE "Slave"
|
||||
|
||||
const StringSet master_running = {MASTER, RUNNING};
|
||||
const StringSet slave_running = {SLAVE, RUNNING};
|
||||
const StringSet running = {RUNNING};
|
||||
const StringSet down = {DOWN};
|
||||
|
||||
void check_status(TestConnections& test, const char* server, const StringSet& expected, const char* message)
|
||||
{
|
||||
StringSet state = test.get_server_status(server);
|
||||
test.assert(state == expected, "%s: %s", message, dump_status(state, expected).c_str());
|
||||
}
|
||||
|
||||
static bool is_running = true;
|
||||
|
||||
void writer_func(TestConnections* test)
|
||||
{
|
||||
while (is_running)
|
||||
{
|
||||
MYSQL* conn = open_conn(test->maxscales->rwsplit_port[0], test->maxscales->IP[0],
|
||||
"test", "test", false);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (execute_query_silent(conn, "INSERT INTO test.t1 VALUES (SELECT SLEEP(0.5))"))
|
||||
{
|
||||
sleep(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mysql_close(conn);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Mariadb_nodes::require_gtid(true);
|
||||
TestConnections test(argc, argv);
|
||||
|
||||
// Create a table and a user and start a thread that does writes
|
||||
test.repl->connect();
|
||||
execute_query(test.repl->nodes[0], "CREATE OR REPLACE TABLE test.t1 (id INT)");
|
||||
execute_query(test.repl->nodes[0], "DROP USER IF EXISTS 'test'@'%%'");
|
||||
execute_query(test.repl->nodes[0], "CREATE USER 'test'@'%%' IDENTIFIED BY 'test'");
|
||||
execute_query(test.repl->nodes[0], "GRANT INSERT, SELECT, UPDATE, DELETE ON *.* TO 'test'@'%%'");
|
||||
test.repl->sync_slaves();
|
||||
std::thread thr(writer_func, &test);
|
||||
|
||||
test.tprintf("Start by having the current master replicate from the external server");
|
||||
test.repl->connect();
|
||||
test.repl->replicate_from(0, 3);
|
||||
sleep(5);
|
||||
check_status(test, "server1", master_running, "server1 should be the master");
|
||||
check_status(test, "server2", slave_running, "server2 should be a slave");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
test.tprintf("Stop server1, expect server2 to be promoted as the master");
|
||||
test.repl->stop_node(0);
|
||||
sleep(10);
|
||||
|
||||
check_status(test, "server1", down, "server1 should be down");
|
||||
check_status(test, "server2", master_running, "server2 should be the master");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
test.tprintf("Configure master-master replication between server2 and the external server");
|
||||
test.repl->replicate_from(1, 3);
|
||||
test.repl->replicate_from(3, 1);
|
||||
sleep(10);
|
||||
check_status(test, "server2", master_running, "server2 should still be the master");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
test.tprintf("Start server1, expect it to rejoin the cluster");
|
||||
test.repl->start_node(0);
|
||||
sleep(10);
|
||||
check_status(test, "server1", slave_running, "server1 should be a slave");
|
||||
check_status(test, "server2", master_running, "server2 should still be the master");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
test.tprintf("Stop server2, expect server1 to be promoted as the master");
|
||||
test.repl->stop_node(1);
|
||||
test.repl->connect();
|
||||
test.repl->replicate_from(0, 3);
|
||||
test.repl->replicate_from(3, 0);
|
||||
sleep(10);
|
||||
|
||||
check_status(test, "server1", master_running, "server1 should be the master");
|
||||
check_status(test, "server2", down, "server2 should be down");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
test.tprintf("Start server2, expect it to rejoin the cluster");
|
||||
test.repl->start_node(1);
|
||||
sleep(10);
|
||||
check_status(test, "server1", master_running, "server1 should still be the master");
|
||||
check_status(test, "server2", slave_running, "server2 should be a slave");
|
||||
check_status(test, "server3", slave_running, "server3 should be a slave");
|
||||
|
||||
// Cleanup
|
||||
is_running = false;
|
||||
thr.join();
|
||||
execute_query(test.repl->nodes[0], "STOP SLAVE; RESET SLAVE ALL;");
|
||||
|
||||
return test.global_result;
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <execinfo.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "mariadb_func.h"
|
||||
#include "maxadmin_operations.h"
|
||||
@ -819,7 +820,6 @@ int TestConnections::start_binlog(int m)
|
||||
binlog = open_conn_no_db(maxscales->binlog_port[m], maxscales->IP[m], repl->user_name, repl->password, ssl);
|
||||
execute_query(binlog, (char *) "stop slave");
|
||||
execute_query(binlog, (char *) "reset slave all");
|
||||
execute_query(binlog, (char *) "reset master");
|
||||
mysql_close(binlog);
|
||||
|
||||
tprintf("Stopping maxscale\n");
|
||||
@ -1112,55 +1112,63 @@ void TestConnections::check_log_err(int m, const char * err_msg, bool expected)
|
||||
|
||||
char * err_log_content;
|
||||
|
||||
tprintf("Getting logs\n");
|
||||
if (verbose)
|
||||
{
|
||||
tprintf("Getting logs");
|
||||
}
|
||||
char sys1[4096];
|
||||
char dest[1024];
|
||||
char log_file[64];
|
||||
set_timeout(100);
|
||||
set_timeout(500);
|
||||
sprintf(dest, "maxscale_log_%03d/", m);
|
||||
sprintf(&sys1[0], "mkdir -p maxscale_log_%03d; rm -f %s*.log",
|
||||
m, dest);
|
||||
//tprintf("Executing: %s\n", sys1);
|
||||
|
||||
system(sys1);
|
||||
set_timeout(50);
|
||||
sprintf(sys1, "%s/*", maxscales->maxscale_log_dir[m]);
|
||||
maxscales->copy_from_node(m, sys1, dest);
|
||||
|
||||
tprintf("Reading maxscale.log\n");
|
||||
sprintf(log_file, "maxscale_log_%03d/maxscale.log", m);
|
||||
if ( ( read_log(log_file, &err_log_content) != 0) || (strlen(err_log_content) < 2) )
|
||||
if (verbose)
|
||||
{
|
||||
tprintf("Reading maxscale1.log\n");
|
||||
tprintf("Reading maxscale.log");
|
||||
}
|
||||
sprintf(log_file, "maxscale_log_%03d/maxscale.log", m);
|
||||
if (read_log(log_file, &err_log_content) != 0 || strlen(err_log_content) < 2)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
tprintf("Reading maxscale1.log");
|
||||
}
|
||||
sprintf(log_file, "maxscale_log_%03d/maxscale1.log", m);
|
||||
free(err_log_content);
|
||||
if (read_log(log_file, &err_log_content) != 0)
|
||||
{
|
||||
add_result(1, "Error reading log\n");
|
||||
add_result(1, "Error reading log");
|
||||
}
|
||||
}
|
||||
//printf("\n\n%s\n\n", err_log_content);
|
||||
|
||||
if (err_log_content != NULL)
|
||||
{
|
||||
if (expected)
|
||||
{
|
||||
if (strstr(err_log_content, err_msg) == NULL)
|
||||
{
|
||||
add_result(1, "There is NO \"%s\" error in the log\n", err_msg);
|
||||
add_result(1, "There is NO \"%s\" error in the log", err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
tprintf("There is proper \"%s \" error in the log\n", err_msg);
|
||||
tprintf("There is a proper \"%s \" error in the log", err_msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(err_log_content, err_msg) != NULL)
|
||||
{
|
||||
add_result(1, "There is UNEXPECTED error \"%s\" error in the log\n", err_msg);
|
||||
add_result(1, "There is an UNEXPECTED \"%s\" error in the log", err_msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
tprintf("There are no unxpected errors \"%s \" error in the log\n", err_msg);
|
||||
tprintf("There are no unxpected \"%s \" errors in the log", err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1877,3 +1885,25 @@ bool TestConnections::test_bad_config(int m, const char *config)
|
||||
return maxscales->ssh_node(m, "cp maxscale.cnf /etc/maxscale.cnf; service maxscale stop; "
|
||||
"maxscale -U maxscale -lstdout &> /dev/null && sleep 1 && pkill -9 maxscale", false) == 0;
|
||||
}
|
||||
|
||||
std::string dump_status(const StringSet& current, const StringSet& expected)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Current status: (";
|
||||
|
||||
for (const auto& a: current)
|
||||
{
|
||||
ss << a << ",";
|
||||
}
|
||||
|
||||
ss << ") Expected status: (";
|
||||
|
||||
for (const auto& a: expected)
|
||||
{
|
||||
ss << a << ",";
|
||||
}
|
||||
|
||||
ss << ")";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
@ -531,4 +531,14 @@ void * timeout_thread(void *ptr );
|
||||
*/
|
||||
void * log_copy_thread(void *ptr );
|
||||
|
||||
/**
|
||||
* Dump two server status sets as strings
|
||||
*
|
||||
* @param current The current status
|
||||
* @param expected The expected status
|
||||
*
|
||||
* @return String form comparison of status sets
|
||||
*/
|
||||
std::string dump_status(const StringSet& current, const StringSet& expected);
|
||||
|
||||
#endif // TESTCONNECTIONS_H
|
||||
|
@ -3,9 +3,9 @@ if (BUILD_QC_MYSQLEMBEDDED)
|
||||
# Include the embedded library headers
|
||||
subdirs(MYSQL_INCLUDE_DIR_ALL ${MYSQL_EMBEDDED_INCLUDE_DIR})
|
||||
foreach(DIR ${MYSQL_INCLUDE_DIR_ALL})
|
||||
include_directories(${DIR})
|
||||
include_directories(BEFORE ${DIR})
|
||||
endforeach()
|
||||
include_directories(${MYSQL_EMBEDDED_INCLUDE_DIR}/..)
|
||||
include_directories(BEFORE ${MYSQL_EMBEDDED_INCLUDE_DIR}/..)
|
||||
|
||||
add_library(qc_mysqlembedded SHARED qc_mysqlembedded.cc)
|
||||
|
||||
|
@ -3526,7 +3526,7 @@ extern "C"
|
||||
{
|
||||
MXS_MODULE_API_QUERY_CLASSIFIER,
|
||||
MXS_MODULE_IN_DEVELOPMENT,
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
MXS_QUERY_CLASSIFIER_VERSION,
|
||||
"Query classifier based upon MySQL Embedded",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
|
@ -5037,7 +5037,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
{
|
||||
MXS_MODULE_API_QUERY_CLASSIFIER,
|
||||
MXS_MODULE_BETA_RELEASE,
|
||||
QUERY_CLASSIFIER_VERSION,
|
||||
MXS_QUERY_CLASSIFIER_VERSION,
|
||||
"Query classifier using sqlite.",
|
||||
"V1.0.0",
|
||||
MXS_NO_MODULE_CAPABILITIES,
|
||||
|
@ -611,6 +611,39 @@ int config_cb(const char* fpath, const struct stat *sb, int typeflag, struct FTW
|
||||
{
|
||||
int rval = 0;
|
||||
|
||||
if (typeflag == FTW_SL) // A symbolic link; let's see what it points to.
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (stat(fpath, &sb) == 0)
|
||||
{
|
||||
int file_type = (sb.st_mode & S_IFMT);
|
||||
|
||||
switch (file_type)
|
||||
{
|
||||
case S_IFREG:
|
||||
// Points to a file; we'll handle that regardless of where the file resides.
|
||||
typeflag = FTW_F;
|
||||
break;
|
||||
|
||||
case S_IFDIR:
|
||||
// Points to a directory; we'll ignore that.
|
||||
MXS_WARNING("Symbolic link %s in configuration directory points to a "
|
||||
"directory; it will be ignored.", fpath);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Points to something else; we'll silently ignore.
|
||||
;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("Could not get information about the symbolic link %s; "
|
||||
"it will be ignored.", fpath);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeflag == FTW_F) // We are only interested in files,
|
||||
{
|
||||
const char* filename = fpath + ftwbuf->base;
|
||||
@ -3651,6 +3684,7 @@ void fix_serverlist(char* value)
|
||||
dest += sep;
|
||||
dest += start;
|
||||
sep = ",";
|
||||
start = strtok_r(NULL, ",", &end);
|
||||
}
|
||||
|
||||
/** The value will always be smaller than the original one or of equal size */
|
||||
|
@ -3204,9 +3204,24 @@ static uint32_t dcb_handler(DCB* dcb, uint32_t events)
|
||||
|
||||
static uint32_t dcb_poll_handler(MXS_POLL_DATA *data, int thread_id, uint32_t events)
|
||||
{
|
||||
uint32_t rval = 0;
|
||||
DCB *dcb = (DCB*)data;
|
||||
|
||||
return dcb_handler(dcb, events);
|
||||
/**
|
||||
* Fake hangup events (e.g. from monitors) can cause a DCB to be closed
|
||||
* before the real events are processed. This makes it look like a closed
|
||||
* DCB is receiving events when in reality the events were received at the
|
||||
* same time the DCB was closed. If a closed DCB receives events they should
|
||||
* be ignored.
|
||||
*
|
||||
* @see FakeEventTask()
|
||||
*/
|
||||
if (dcb->n_close == 0)
|
||||
{
|
||||
rval = dcb_handler(dcb, events);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static bool dcb_is_still_valid(DCB* target, int id)
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <maxscale/housekeeper.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/maxscale.h>
|
||||
#include <maxscale/mysql_utils.h>
|
||||
#include <maxscale/paths.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include <maxscale/server.h>
|
||||
@ -188,6 +189,8 @@ static bool modules_process_init();
|
||||
static void modules_process_finish();
|
||||
static void disable_module_unloading(const char* arg);
|
||||
static void enable_module_unloading(const char* arg);
|
||||
static void enable_statement_logging(const char* arg);
|
||||
static void disable_statement_logging(const char* arg);
|
||||
static void redirect_output_to_file(const char* arg);
|
||||
static bool user_is_acceptable(const char* specified_user);
|
||||
static bool init_sqlite3();
|
||||
@ -217,6 +220,14 @@ const DEBUG_ARGUMENT debug_arguments[] =
|
||||
"redirect-output-to-file", redirect_output_to_file,
|
||||
"redirect stdout and stderr to the file given as an argument"
|
||||
},
|
||||
{
|
||||
"enable-statement-logging", enable_statement_logging,
|
||||
"enable the logging of SQL statements sent by MaxScale to the servers"
|
||||
},
|
||||
{
|
||||
"disable-statement-logging", disable_statement_logging,
|
||||
"disable the logging of SQL statements sent by MaxScale to the servers"
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
@ -3185,6 +3196,16 @@ static void disable_module_unloading(const char* arg)
|
||||
unload_modules_at_exit = false;
|
||||
}
|
||||
|
||||
static void enable_statement_logging(const char* arg)
|
||||
{
|
||||
mxs_mysql_set_log_statements(true);
|
||||
}
|
||||
|
||||
static void disable_statement_logging(const char* arg)
|
||||
{
|
||||
mxs_mysql_set_log_statements(false);
|
||||
}
|
||||
|
||||
static void redirect_output_to_file(const char* arg)
|
||||
{
|
||||
if (arg)
|
||||
|
@ -32,6 +32,12 @@
|
||||
#include <maxscale/alloc.h>
|
||||
#include <maxscale/json_api.h>
|
||||
#include <maxscale/modulecmd.h>
|
||||
#include <maxscale/protocol.h>
|
||||
#include <maxscale/router.h>
|
||||
#include <maxscale/filter.h>
|
||||
#include <maxscale/authenticator.h>
|
||||
#include <maxscale/monitor.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
#include "internal/modules.h"
|
||||
#include "internal/config.h"
|
||||
@ -78,6 +84,56 @@ static LOADED_MODULE* register_module(const char *module,
|
||||
MXS_MODULE *mod_info);
|
||||
static void unregister_module(const char *module);
|
||||
|
||||
static bool api_version_mismatch(const MXS_MODULE *mod_info, const char* module)
|
||||
{
|
||||
bool rval = false;
|
||||
MXS_MODULE_VERSION api = {};
|
||||
|
||||
switch (mod_info->modapi)
|
||||
{
|
||||
case MXS_MODULE_API_PROTOCOL:
|
||||
api = MXS_PROTOCOL_VERSION;
|
||||
break;
|
||||
|
||||
case MXS_MODULE_API_AUTHENTICATOR:
|
||||
api = MXS_AUTHENTICATOR_VERSION;
|
||||
break;
|
||||
|
||||
case MXS_MODULE_API_ROUTER:
|
||||
api = MXS_ROUTER_VERSION;
|
||||
break;
|
||||
|
||||
case MXS_MODULE_API_MONITOR:
|
||||
api = MXS_MONITOR_VERSION;
|
||||
break;
|
||||
|
||||
case MXS_MODULE_API_FILTER:
|
||||
api = MXS_FILTER_VERSION;
|
||||
break;
|
||||
|
||||
case MXS_MODULE_API_QUERY_CLASSIFIER:
|
||||
api = MXS_QUERY_CLASSIFIER_VERSION;
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_ERROR("Unknown module type: 0x%02hhx", mod_info->modapi);
|
||||
ss_dassert(!true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (api.major != mod_info->api_version.major ||
|
||||
api.minor != mod_info->api_version.minor ||
|
||||
api.patch != mod_info->api_version.patch)
|
||||
{
|
||||
MXS_ERROR("API version mismatch for '%s': Need version %d.%d.%d, have %d.%d.%d",
|
||||
module, api.major, api.minor, api.patch, mod_info->api_version.major,
|
||||
mod_info->api_version.minor, mod_info->api_version.patch);
|
||||
rval = true;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static bool check_module(const MXS_MODULE *mod_info, const char *type, const char *module)
|
||||
{
|
||||
bool success = true;
|
||||
@ -118,6 +174,12 @@ static bool check_module(const MXS_MODULE *mod_info, const char *type, const cha
|
||||
MXS_ERROR("Module '%s' does not implement the query classifier API.", module);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (api_version_mismatch(mod_info, module))
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (mod_info->version == NULL)
|
||||
{
|
||||
MXS_ERROR("Module '%s' does not define a version string", module);
|
||||
|
@ -2428,7 +2428,7 @@ static bool journal_is_stale(MXS_MONITOR *monitor, time_t max_age)
|
||||
return is_stale;
|
||||
}
|
||||
|
||||
MXS_MONITORED_SERVER* mon_get_monitored_server(MXS_MONITOR* mon, SERVER* search_server)
|
||||
MXS_MONITORED_SERVER* mon_get_monitored_server(const MXS_MONITOR* mon, SERVER* search_server)
|
||||
{
|
||||
ss_dassert(mon && search_server);
|
||||
for (MXS_MONITORED_SERVER* iter = mon->monitored_servers; iter != NULL; iter = iter->next)
|
||||
@ -2439,4 +2439,46 @@ MXS_MONITORED_SERVER* mon_get_monitored_server(MXS_MONITOR* mon, SERVER* search_
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mon_config_get_servers(const MXS_CONFIG_PARAMETER* params, const char* key, const MXS_MONITOR* mon,
|
||||
MXS_MONITORED_SERVER*** monitored_servers_out)
|
||||
{
|
||||
ss_dassert(*monitored_servers_out == NULL);
|
||||
SERVER** servers = NULL;
|
||||
int servers_size = config_get_server_list(params, key, &servers);
|
||||
int rval = 0;
|
||||
// All servers in the array must be monitored by the given monitor.
|
||||
if (servers_size > 0)
|
||||
{
|
||||
MXS_MONITORED_SERVER** monitored_array =
|
||||
(MXS_MONITORED_SERVER**)MXS_CALLOC(servers_size, sizeof(MXS_MONITORED_SERVER*));
|
||||
bool error = false;
|
||||
for (int i = 0; i < servers_size && !error; i++)
|
||||
{
|
||||
MXS_MONITORED_SERVER* mon_serv = mon_get_monitored_server(mon, servers[i]);
|
||||
if (mon_serv != NULL)
|
||||
{
|
||||
monitored_array[i] = mon_serv;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Server '%s' is not monitored by monitor '%s'.", servers[i]->unique_name, mon->name);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
MXS_FREE(servers);
|
||||
|
||||
if (error)
|
||||
{
|
||||
MXS_FREE(monitored_array);
|
||||
rval = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*monitored_servers_out = monitored_array;
|
||||
rval = servers_size;
|
||||
}
|
||||
}
|
||||
return rval;
|
||||
}
|
@ -32,6 +32,21 @@
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/log_manager.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct THIS_UNIT
|
||||
{
|
||||
bool log_statements; // Should all statements sent to server be logged?
|
||||
};
|
||||
|
||||
static THIS_UNIT this_unit =
|
||||
{
|
||||
false
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate the length of a length-encoded integer in bytes
|
||||
*
|
||||
@ -221,6 +236,19 @@ int mxs_mysql_query(MYSQL* conn, const char* query)
|
||||
rc = mysql_query(conn, query);
|
||||
}
|
||||
|
||||
if (this_unit.log_statements)
|
||||
{
|
||||
const char* host;
|
||||
if (mariadb_get_info(conn, MARIADB_CONNECTION_HOST, &host) != 0)
|
||||
{
|
||||
// No idea about the host, but let's use something that looks like
|
||||
// an IP-address as a placeholder.
|
||||
host = "0.0.0.0";
|
||||
}
|
||||
|
||||
MXS_NOTICE("SQL(%s): %d, \"%s\"", host, rc, query);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -377,3 +405,13 @@ void mxs_mysql_set_server_version(MYSQL* mysql, SERVER* server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mxs_mysql_set_log_statements(bool enable)
|
||||
{
|
||||
this_unit.log_statements = enable;
|
||||
}
|
||||
|
||||
bool mxs_mysql_get_log_statements()
|
||||
{
|
||||
return this_unit.log_statements;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <sys/time.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
@ -85,6 +85,12 @@ enum slave_down_setting_t
|
||||
REJECT_DOWN
|
||||
};
|
||||
|
||||
enum print_repl_warnings_t
|
||||
{
|
||||
WARNINGS_ON,
|
||||
WARNINGS_OFF
|
||||
};
|
||||
|
||||
static void monitorMain(void *);
|
||||
static void *startMonitor(MXS_MONITOR *, const MXS_CONFIG_PARAMETER*);
|
||||
static void stopMonitor(MXS_MONITOR *);
|
||||
@ -132,6 +138,7 @@ static const char CN_FAILOVER_TIMEOUT[] = "failover_timeout";
|
||||
static const char CN_SWITCHOVER_TIMEOUT[] = "switchover_timeout";
|
||||
static const char CN_AUTO_REJOIN[] = "auto_rejoin";
|
||||
static const char CN_FAILCOUNT[] = "failcount";
|
||||
static const char CN_NO_PROMOTE_SERVERS[] = "servers_no_promotion";
|
||||
|
||||
// Parameters for master failure verification and timeout
|
||||
static const char CN_VERIFY_MASTER_FAILURE[] = "verify_master_failure";
|
||||
@ -893,6 +900,7 @@ extern "C"
|
||||
{CN_VERIFY_MASTER_FAILURE, MXS_MODULE_PARAM_BOOL, "true"},
|
||||
{CN_MASTER_FAILURE_TIMEOUT, MXS_MODULE_PARAM_COUNT, DEFAULT_MASTER_FAILURE_TIMEOUT},
|
||||
{CN_AUTO_REJOIN, MXS_MODULE_PARAM_BOOL, "false"},
|
||||
{CN_NO_PROMOTE_SERVERS, MXS_MODULE_PARAM_SERVERLIST},
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
@ -985,7 +993,24 @@ static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_
|
||||
return rval;
|
||||
}
|
||||
|
||||
/*lint +e14 */
|
||||
/**
|
||||
* Is the server in the excluded list
|
||||
*
|
||||
* @param handle Cluster monitor
|
||||
* @param server Server to test
|
||||
* @return True if server is in the excluded-list of the monitor.
|
||||
*/
|
||||
static bool server_is_excluded(const MYSQL_MONITOR *handle, const MXS_MONITORED_SERVER* server)
|
||||
{
|
||||
for (int i = 0; i < handle->n_excluded; i++)
|
||||
{
|
||||
if (handle->excluded_servers[i] == server)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the instance of the monitor, returning a handle on the monitor.
|
||||
@ -999,14 +1024,17 @@ static bool set_replication_credentials(MYSQL_MONITOR *handle, const MXS_CONFIG_
|
||||
static void *
|
||||
startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
|
||||
{
|
||||
bool error = false;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR*) monitor->handle;
|
||||
|
||||
if (handle)
|
||||
{
|
||||
handle->shutdown = 0;
|
||||
MXS_FREE(handle->script);
|
||||
MXS_FREE(handle->replication_user);
|
||||
MXS_FREE(handle->replication_password);
|
||||
MXS_FREE(handle->excluded_servers);
|
||||
handle->excluded_servers = NULL;
|
||||
handle->n_excluded = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1052,7 +1080,13 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
|
||||
handle->master_failure_timeout = config_get_integer(params, CN_MASTER_FAILURE_TIMEOUT);
|
||||
handle->auto_rejoin = config_get_bool(params, CN_AUTO_REJOIN);
|
||||
|
||||
bool error = false;
|
||||
handle->excluded_servers = NULL;
|
||||
handle->n_excluded = mon_config_get_servers(params, CN_NO_PROMOTE_SERVERS, monitor,
|
||||
&handle->excluded_servers);
|
||||
if (handle->n_excluded < 0)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (!set_replication_credentials(handle, params))
|
||||
{
|
||||
@ -1075,6 +1109,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
|
||||
{
|
||||
hashtable_free(handle->server_info);
|
||||
MXS_FREE(handle->script);
|
||||
MXS_FREE(handle->excluded_servers);
|
||||
MXS_FREE(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
@ -1167,7 +1202,10 @@ static void diagnostics(DCB *dcb, const MXS_MONITOR *mon)
|
||||
dcb_printf(dcb, "Master ID: %" PRId64 "\n", serv_info->slave_status.master_server_id);
|
||||
dcb_printf(dcb, "Master binlog file: %s\n", serv_info->slave_status.master_log_file.c_str());
|
||||
dcb_printf(dcb, "Master binlog position: %lu\n", serv_info->slave_status.read_master_log_pos);
|
||||
|
||||
if (serv_info->slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN)
|
||||
{
|
||||
dcb_printf(dcb, "Gtid_IO_Pos: %s\n", serv_info->slave_status.gtid_io_pos.to_string().c_str());
|
||||
}
|
||||
if (handle->multimaster)
|
||||
{
|
||||
dcb_printf(dcb, "Master group: %d\n", serv_info->group);
|
||||
@ -1229,7 +1267,11 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon)
|
||||
json_string(serv_info->slave_status.master_log_file.c_str()));
|
||||
json_object_set_new(srv, "master_binlog_position",
|
||||
json_integer(serv_info->slave_status.read_master_log_pos));
|
||||
|
||||
if (serv_info->slave_status.gtid_io_pos.server_id != SERVER_ID_UNKNOWN)
|
||||
{
|
||||
json_object_set_new(srv, "gtid_io_pos",
|
||||
json_string(serv_info->slave_status.gtid_io_pos.to_string().c_str()));
|
||||
}
|
||||
if (handle->multimaster)
|
||||
{
|
||||
json_object_set_new(srv, "master_group", json_integer(serv_info->group));
|
||||
@ -1953,8 +1995,10 @@ bool standalone_master_required(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db)
|
||||
* @param handle Monitor instance
|
||||
* @param db Monitor servers
|
||||
*/
|
||||
void set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db)
|
||||
bool set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
while (db)
|
||||
{
|
||||
if (SERVER_IS_RUNNING(db->server))
|
||||
@ -1972,6 +2016,7 @@ void set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db)
|
||||
monitor_set_pending_status(db, SERVER_MASTER | SERVER_STALE_STATUS);
|
||||
monitor_clear_pending_status(db, SERVER_SLAVE);
|
||||
handle->master = db;
|
||||
rval = true;
|
||||
}
|
||||
else if (!handle->allow_cluster_recovery)
|
||||
{
|
||||
@ -1980,6 +2025,8 @@ void set_standalone_master(MYSQL_MONITOR *handle, MXS_MONITORED_SERVER *db)
|
||||
}
|
||||
db = db->next;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool failover_not_possible(MYSQL_MONITOR* handle)
|
||||
@ -2294,8 +2341,12 @@ monitorMain(void *arg)
|
||||
{
|
||||
if (standalone_master_required(handle, mon->monitored_servers))
|
||||
{
|
||||
/** Other servers have died, set last remaining server as master */
|
||||
set_standalone_master(handle, mon->monitored_servers);
|
||||
// Other servers have died, set last remaining server as master
|
||||
if (set_standalone_master(handle, mon->monitored_servers))
|
||||
{
|
||||
// Update the root_master to point to the standalone master
|
||||
root_master = handle->master;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2303,9 +2354,10 @@ monitorMain(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
if (root_master)
|
||||
if (root_master && SERVER_IS_MASTER(root_master->server))
|
||||
{
|
||||
// Clear slave and stale slave status bits from current master
|
||||
server_clear_status_nolock(root_master->server, SERVER_SLAVE | SERVER_STALE_SLAVE);
|
||||
monitor_clear_pending_status(root_master, SERVER_SLAVE | SERVER_STALE_SLAVE);
|
||||
|
||||
/**
|
||||
@ -2313,16 +2365,18 @@ monitorMain(void *arg)
|
||||
* This allows parts of a multi-tiered replication setup to be used
|
||||
* in MaxScale.
|
||||
*/
|
||||
if (SERVER_IS_SLAVE_OF_EXTERNAL_MASTER(root_master->server) &&
|
||||
SERVER_IS_MASTER(root_master->server) && handle->ignore_external_masters)
|
||||
if (handle->ignore_external_masters)
|
||||
{
|
||||
monitor_clear_pending_status(root_master,
|
||||
SERVER_SLAVE | SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
server_clear_status_nolock(root_master->server,
|
||||
SERVER_SLAVE | SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
monitor_clear_pending_status(root_master, SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
server_clear_status_nolock(root_master->server, SERVER_SLAVE_OF_EXTERNAL_MASTER);
|
||||
}
|
||||
}
|
||||
|
||||
ss_dassert(handle->master == root_master);
|
||||
ss_dassert(!root_master ||
|
||||
((root_master->server->status & (SERVER_SLAVE | SERVER_MASTER))
|
||||
!= (SERVER_SLAVE | SERVER_MASTER)));
|
||||
|
||||
/**
|
||||
* After updating the status of all servers, check if monitor events
|
||||
* need to be launched.
|
||||
@ -3262,20 +3316,25 @@ static MySqlServerInfo* update_slave_info(MYSQL_MONITOR* mon, MXS_MONITORED_SERV
|
||||
*
|
||||
* @param server Server to check
|
||||
* @param server_info Server info
|
||||
* @param print_on Print warnings or not
|
||||
* @return True if log_bin is on
|
||||
*/
|
||||
static bool check_replication_settings(const MXS_MONITORED_SERVER* server, MySqlServerInfo* server_info)
|
||||
static bool check_replication_settings(const MXS_MONITORED_SERVER* server, MySqlServerInfo* server_info,
|
||||
print_repl_warnings_t print_warnings = WARNINGS_ON)
|
||||
{
|
||||
bool rval = true;
|
||||
const char* servername = server->server->unique_name;
|
||||
if (server_info->rpl_settings.log_bin == false)
|
||||
{
|
||||
const char NO_BINLOG[] =
|
||||
"Slave '%s' has binary log disabled and is not a valid promotion candidate.";
|
||||
MXS_WARNING(NO_BINLOG, servername);
|
||||
if (print_warnings == WARNINGS_ON)
|
||||
{
|
||||
const char NO_BINLOG[] =
|
||||
"Slave '%s' has binary log disabled and is not a valid promotion candidate.";
|
||||
MXS_WARNING(NO_BINLOG, servername);
|
||||
}
|
||||
rval = false;
|
||||
}
|
||||
else
|
||||
else if (print_warnings == WARNINGS_ON)
|
||||
{
|
||||
if (server_info->rpl_settings.gtid_strict_mode == false)
|
||||
{
|
||||
@ -3334,86 +3393,132 @@ bool switchover_check_preferred_master(MYSQL_MONITOR* mon, MXS_MONITORED_SERVER*
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the candidate a better choice for master than the previous best?
|
||||
*
|
||||
* @param current_best_info Server info of current best choice
|
||||
* @param candidate_info Server info of new candidate
|
||||
* @return True if candidate is better
|
||||
*/
|
||||
bool is_candidate_better(const MySqlServerInfo* current_best_info, const MySqlServerInfo* candidate_info)
|
||||
{
|
||||
uint64_t cand_io = candidate_info->slave_status.gtid_io_pos.sequence;
|
||||
uint64_t cand_processed = candidate_info->gtid_current_pos.sequence;
|
||||
uint64_t curr_io = current_best_info->slave_status.gtid_io_pos.sequence;
|
||||
uint64_t curr_processed = current_best_info->gtid_current_pos.sequence;
|
||||
bool cand_updates = candidate_info->rpl_settings.log_slave_updates;
|
||||
bool curr_updates = current_best_info->rpl_settings.log_slave_updates;
|
||||
bool is_better = false;
|
||||
// Accept a slave with a later event in relay log.
|
||||
if (cand_io > curr_io)
|
||||
{
|
||||
is_better = true;
|
||||
}
|
||||
// If io sequences are identical, the slave with more events processed wins.
|
||||
else if (cand_io == curr_io)
|
||||
{
|
||||
if (cand_processed > curr_processed)
|
||||
{
|
||||
is_better = true;
|
||||
}
|
||||
// Finally, if binlog positions are identical, prefer a slave with log_slave_updates.
|
||||
else if (cand_processed == curr_processed && cand_updates && !curr_updates)
|
||||
{
|
||||
is_better = true;
|
||||
}
|
||||
}
|
||||
return is_better;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a new master. Also add slaves which should be redirected to an array.
|
||||
*
|
||||
* @param mon The monitor
|
||||
* @param out_slaves Vector for storing slave servers, can be NULL
|
||||
* @param out_slaves Vector for storing slave servers.
|
||||
* @param err_out json object for error printing. Can be NULL.
|
||||
* @return The found master, or NULL if not found
|
||||
*/
|
||||
MXS_MONITORED_SERVER* select_new_master(MYSQL_MONITOR* mon,
|
||||
ServerVector* slaves_out,
|
||||
json_t** err_out)
|
||||
MXS_MONITORED_SERVER* select_new_master(MYSQL_MONITOR* mon, ServerVector* slaves_out, json_t** err_out)
|
||||
{
|
||||
ss_dassert(slaves_out && slaves_out->size() == 0);
|
||||
/* Select a new master candidate. Selects the one with the latest event in relay log.
|
||||
* If multiple slaves have same number of events, select the one with most processed events. */
|
||||
MXS_MONITORED_SERVER* new_master = NULL;
|
||||
MySqlServerInfo* new_master_info = NULL;
|
||||
MXS_MONITORED_SERVER* current_best = NULL;
|
||||
MySqlServerInfo* current_best_info = NULL;
|
||||
// Servers that cannot be selected because of exclusion, but seem otherwise ok.
|
||||
ServerVector valid_but_excluded;
|
||||
// Index of the current best candidate in slaves_out
|
||||
int master_vector_index = -1;
|
||||
|
||||
for (MXS_MONITORED_SERVER *cand = mon->monitor->monitored_servers; cand; cand = cand->next)
|
||||
{
|
||||
// If a server cannot be connected to, it won't be considered for promotion or redirected.
|
||||
// Do not worry about the exclusion list yet, querying the excluded servers is ok.
|
||||
MySqlServerInfo* cand_info = update_slave_info(mon, cand);
|
||||
if (cand_info)
|
||||
{
|
||||
if (slaves_out)
|
||||
slaves_out->push_back(cand);
|
||||
// Check that server is not in the exclusion list while still being a valid choice.
|
||||
if (server_is_excluded(mon, cand) && check_replication_settings(cand, cand_info, WARNINGS_OFF))
|
||||
{
|
||||
slaves_out->push_back(cand);
|
||||
valid_but_excluded.push_back(cand);
|
||||
const char CANNOT_SELECT[] = "Promotion candidate '%s' is excluded from new "
|
||||
"master selection.";
|
||||
MXS_INFO(CANNOT_SELECT, cand->server->unique_name);
|
||||
}
|
||||
if (check_replication_settings(cand, cand_info))
|
||||
else if (check_replication_settings(cand, cand_info))
|
||||
{
|
||||
bool select_this = false;
|
||||
// If no candidate yet, accept any slave. Slaves have already been checked to use gtid.
|
||||
if (new_master == NULL)
|
||||
// If no new master yet, accept any valid candidate. Otherwise check.
|
||||
if (current_best == NULL || is_candidate_better(current_best_info, cand_info))
|
||||
{
|
||||
select_this = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint64_t cand_io = cand_info->slave_status.gtid_io_pos.sequence;
|
||||
uint64_t cand_processed = cand_info->gtid_current_pos.sequence;
|
||||
uint64_t master_io = new_master_info->slave_status.gtid_io_pos.sequence;
|
||||
uint64_t master_processed = new_master_info->gtid_current_pos.sequence;
|
||||
bool cand_updates = cand_info->rpl_settings.log_slave_updates;
|
||||
bool master_updates = new_master_info->rpl_settings.log_slave_updates;
|
||||
// Otherwise accept a slave with a later event in relay log.
|
||||
if (cand_io > master_io ||
|
||||
// If io sequences are identical, the slave with more events processed wins.
|
||||
(cand_io == master_io && (cand_processed > master_processed ||
|
||||
// Finally, if binlog positions are identical,
|
||||
// prefer a slave with log_slave_updates.
|
||||
(cand_processed == master_processed &&
|
||||
cand_updates && !master_updates))))
|
||||
{
|
||||
select_this = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (select_this)
|
||||
{
|
||||
new_master = cand;
|
||||
new_master_info = cand_info;
|
||||
if (slaves_out)
|
||||
{
|
||||
master_vector_index = slaves_out->size() - 1;
|
||||
}
|
||||
// The server has been selected for promotion, for now.
|
||||
current_best = cand;
|
||||
current_best_info = cand_info;
|
||||
master_vector_index = slaves_out->size() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_master && slaves_out)
|
||||
if (current_best)
|
||||
{
|
||||
// Remove the selected master from the vector.
|
||||
ServerVector::iterator remove_this = slaves_out->begin();
|
||||
remove_this += master_vector_index;
|
||||
slaves_out->erase(remove_this);
|
||||
}
|
||||
if (new_master == NULL)
|
||||
|
||||
// Check if any of the excluded servers would be better than the best candidate.
|
||||
for (ServerVector::const_iterator iter = valid_but_excluded.begin();
|
||||
iter != valid_but_excluded.end();
|
||||
iter++)
|
||||
{
|
||||
MySqlServerInfo* excluded_info = get_server_info(mon, *iter);
|
||||
const char* excluded_name = (*iter)->server->unique_name;
|
||||
if (current_best == NULL)
|
||||
{
|
||||
const char EXCLUDED_ONLY_CAND[] = "Server '%s' is a viable choice for new master, "
|
||||
"but cannot be selected as it's excluded.";
|
||||
MXS_WARNING(EXCLUDED_ONLY_CAND, excluded_name);
|
||||
break;
|
||||
}
|
||||
else if (is_candidate_better(current_best_info, excluded_info))
|
||||
{
|
||||
// Print a warning if this server is actually a better candidate than the previous
|
||||
// best.
|
||||
const char EXCLUDED_CAND[] = "Server '%s' is superior to current "
|
||||
"best candidate '%s', but cannot be selected as it's excluded. This may lead to "
|
||||
"loss of data if '%s' is ahead of other servers.";
|
||||
MXS_WARNING(EXCLUDED_CAND, excluded_name, current_best->server->unique_name, excluded_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_best == NULL)
|
||||
{
|
||||
PRINT_MXS_JSON_ERROR(err_out, "No suitable promotion candidate found.");
|
||||
}
|
||||
return new_master;
|
||||
return current_best;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +73,9 @@ typedef struct
|
||||
int64_t master_gtid_domain; /**< Gtid domain currently used by the master */
|
||||
bool auto_rejoin; /**< Attempt to start slave replication on standalone servers or servers
|
||||
replicating from the wrong master. */
|
||||
int n_excluded; /**< Number of excluded servers */
|
||||
MXS_MONITORED_SERVER** excluded_servers; /**< Servers banned for master promotion during auto-failover. */
|
||||
|
||||
MXS_MONITOR* monitor;
|
||||
} MYSQL_MONITOR;
|
||||
|
||||
|
@ -1276,8 +1276,6 @@ static void clientReply(MXS_ROUTER *instance,
|
||||
*/
|
||||
if (backend->session_command_count())
|
||||
{
|
||||
check_session_command_reply(writebuf, backend);
|
||||
|
||||
/** This discards all responses that have already been sent to the client */
|
||||
bool rconn = false;
|
||||
process_sescmd_response(rses, backend, &writebuf, &rconn);
|
||||
|
@ -283,34 +283,6 @@ void closed_session_reply(GWBUF *querybuf)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Uses MySQL specific mechanisms
|
||||
*/
|
||||
/**
|
||||
* @brief Check the reply from a backend server to a session command
|
||||
*
|
||||
* If the reply is an error, a message is logged.
|
||||
*
|
||||
* @param buffer Query buffer containing reply data
|
||||
* @param backend Router session data for a backend server
|
||||
*/
|
||||
void check_session_command_reply(GWBUF *buffer, SRWBackend& backend)
|
||||
{
|
||||
if (MYSQL_IS_ERROR_PACKET(((uint8_t *)GWBUF_DATA(buffer))))
|
||||
{
|
||||
size_t replylen = MYSQL_GET_PAYLOAD_LEN(GWBUF_DATA(buffer));
|
||||
char replybuf[replylen];
|
||||
gwbuf_copy_data(buffer, 0, gwbuf_length(buffer), (uint8_t*)replybuf);
|
||||
std::string err;
|
||||
std::string msg;
|
||||
err.append(replybuf + 8, 5);
|
||||
msg.append(replybuf + 13, replylen - 4 - 5);
|
||||
|
||||
MXS_ERROR("Failed to execute session command in %s. Error was: %s %s",
|
||||
backend->uri(), err.c_str(), msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send an error message to the client telling that the server is in read only mode
|
||||
*
|
||||
|
@ -1045,11 +1045,17 @@ static void log_master_routing_failure(RWSplitSession *rses, bool found,
|
||||
sprintf(errmsg, "Session is in read-only mode because it was created "
|
||||
"when no master was available");
|
||||
}
|
||||
else if (old_master && !old_master->in_use())
|
||||
{
|
||||
sprintf(errmsg, "Was supposed to route to master but the master connection is %s",
|
||||
old_master->is_closed() ? "closed" : "not in a suitable state");
|
||||
ss_dassert(old_master->is_closed());
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dassert(false); // A session should always have a master reference
|
||||
sprintf(errmsg, "Was supposed to route to master but couldn't "
|
||||
"find master in a suitable state");
|
||||
"find original master connection");
|
||||
ss_dassert(!true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,9 +76,10 @@ void process_sescmd_response(RWSplitSession* rses, SRWBackend& backend,
|
||||
|
||||
if (rses->sescmd_responses[id] != cmd)
|
||||
{
|
||||
MXS_ERROR("Slave server '%s': response differs from master's response. "
|
||||
"Closing connection due to inconsistent session state.",
|
||||
backend->name());
|
||||
MXS_WARNING("Slave server '%s': response (0x%02hhx) differs "
|
||||
"from master's response(0x%02hhx). Closing slave "
|
||||
"connection due to inconsistent session state.",
|
||||
backend->name(), cmd, rses->sescmd_responses[id]);
|
||||
backend->close(mxs::Backend::CLOSE_FATAL);
|
||||
*pReconnect = true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user