Added code for MySQL 5.1 replication detection.

This commit is contained in:
Markus Makela
2015-06-30 18:50:26 +03:00
parent 6f343ff57b
commit 8a0b399896
2 changed files with 400 additions and 252 deletions

View File

@ -156,6 +156,7 @@ startMonitor(void *arg, void* opt)
handle->detectStaleMaster = 0; handle->detectStaleMaster = 0;
handle->master = NULL; handle->master = NULL;
handle->script = NULL; handle->script = NULL;
handle->mysql51_replication = false;
memset(handle->events,false,sizeof(handle->events)); memset(handle->events,false,sizeof(handle->events));
spinlock_init(&handle->lock); spinlock_init(&handle->lock);
} }
@ -199,6 +200,10 @@ startMonitor(void *arg, void* opt)
else else
have_events = true; have_events = true;
} }
else if(!strcmp(params->name,"mysql51_only"))
{
handle->mysql51_replication = config_truth_value(params->value);
}
params = params->next; params = params->next;
} }
if(script_error) if(script_error)
@ -281,7 +286,281 @@ static void diagnostics(DCB *dcb, void *arg)
} }
dcb_printf(dcb, "\n"); dcb_printf(dcb, "\n");
} }
/**
* Connect to a database
* @param mon Monitor
* @param database Monitored database
* @return true if connection was successful, false if there was an error
*/
static inline bool connect_to_db(MONITOR* mon,MONITOR_SERVERS *database)
{
char *uname = mon->user;
char *passwd = mon->password;
char *dpwd = decryptPassword(passwd);
int connect_timeout = mon->connect_timeout;
int read_timeout = mon->read_timeout;
int write_timeout = mon->write_timeout;
if(database->con)
mysql_close(database->con);
database->con = mysql_init(NULL);
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
return (mysql_real_connect(database->con,
database->server->name,
uname,
dpwd,
NULL,
database->server->port,
NULL,
0) != NULL);
}
inline void monitor_mysql100_db(MONITOR_SERVERS* database)
{
bool isslave = false;
MYSQL_RES* result;
MYSQL_ROW row;
if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
int i = 0;
long master_id = -1;
if(mysql_field_count(database->con) < 42)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" "
"returned less than the expected amount of columns. Expected 42 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
if (strncmp(row[12], "Yes", 3) == 0
&& strncmp(row[13], "Yes", 3) == 0) {
isslave += 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[12], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[41]);
if (master_id == 0)
master_id = -1;
}
i++;
}
/* store master_id of current node */
memcpy(&database->server->master_id, &master_id, sizeof(long));
mysql_free_result(result);
/* If all configured slaves are running set this node as slave */
if (isslave > 0 && isslave == i)
isslave = 1;
else
isslave = 0;
}
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER
* will be assigned in the monitorMain() via get_replication_tree() routine
*/
/* Set the Slave Role */
if (isslave)
{
monitor_set_pending_status(database, SERVER_SLAVE);
/* Avoid any possible stale Master state */
monitor_clear_pending_status(database, SERVER_MASTER);
} else {
/* Avoid any possible Master/Slave stale state */
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
}
}
inline void monitor_mysql55_db(MONITOR_SERVERS* database)
{
bool isslave = false;
MYSQL_RES* result;
MYSQL_ROW row;
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
long master_id = -1;
if(mysql_field_count(database->con) < 40)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
"returned less than the expected amount of columns. Expected 40 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
if (strncmp(row[10], "Yes", 3) == 0
&& strncmp(row[11], "Yes", 3) == 0) {
isslave = 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[10], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[39]);
if (master_id == 0)
master_id = -1;
}
}
/* store master_id of current node */
memcpy(&database->server->master_id, &master_id, sizeof(long));
mysql_free_result(result);
}
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER
* will be assigned in the monitorMain() via get_replication_tree() routine
*/
/* Set the Slave Role */
if (isslave)
{
monitor_set_pending_status(database, SERVER_SLAVE);
/* Avoid any possible stale Master state */
monitor_clear_pending_status(database, SERVER_MASTER);
} else {
/* Avoid any possible Master/Slave stale state */
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
}
}
inline void monitor_mysql51_db(MONITOR_SERVERS* database)
{
bool isslave = false;
MYSQL_RES* result;
MYSQL_ROW row;
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 38)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
"returned less than the expected amount of columns. Expected 38 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
if (strncmp(row[10], "Yes", 3) == 0
&& strncmp(row[11], "Yes", 3) == 0) {
isslave = 1;
}
}
mysql_free_result(result);
}
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER
* will be assigned in the monitorMain() via get_replication_tree() routine
*/
/* Set the Slave Role */
if (isslave)
{
monitor_set_pending_status(database, SERVER_SLAVE);
/* Avoid any possible stale Master state */
monitor_clear_pending_status(database, SERVER_MASTER);
} else {
/* Avoid any possible Master/Slave stale state */
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
}
}
static MONITOR_SERVERS *build_mysql51_replication_tree(MONITOR *mon)
{
MONITOR_SERVERS* database = mon->databases;
while(database)
{
bool ismaster = false;
MYSQL_RES* result;
MYSQL_ROW row;
unsigned long *slaves;
int nslaves = 0;
if(database->con)
{
if (mysql_query(database->con, "SHOW SLAVE HOSTS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 4)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW SLAVE HOSTS\" "
"returned less than the expected amount of columns. Expected 4 columns."
" MySQL Version: %s",version_str);
return;
}
if(mysql_num_rows(result) > 0)
{
ismaster = true;
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
database->server->slaves[nslaves] = atol(row[0]);
nslaves++;
}
}
mysql_free_result(result);
}
/* Set the Slave Role */
if (ismaster)
{
monitor_set_pending_status(database, SERVER_MASTER);
}
}
database = database->next;
}
}
/** /**
* Monitor an individual server * Monitor an individual server
* *
@ -292,271 +571,135 @@ static void
monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database) monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
{ {
MYSQL_MONITOR* handle = mon->handle; MYSQL_MONITOR* handle = mon->handle;
MYSQL_ROW row; MYSQL_ROW row;
MYSQL_RES *result; MYSQL_RES *result;
int isslave = 0; int isslave = 0;
char *uname = mon->user; char *uname = mon->user;
char *passwd = mon->password; char *passwd = mon->password;
unsigned long int server_version = 0; unsigned long int server_version = 0;
char *server_string; char *server_string;
if (database->server->monuser != NULL) if (database->server->monuser != NULL)
{
uname = database->server->monuser;
passwd = database->server->monpw;
}
if (uname == NULL)
return;
/* Don't probe servers in maintenance mode */
if (SERVER_IN_MAINT(database->server))
return;
/** Store previous status */
database->mon_prev_status = database->server->status;
if (database->con == NULL || mysql_ping(database->con) != 0)
{
if(connect_to_db(mon,database))
{ {
uname = database->server->monuser; server_clear_status(database->server, SERVER_AUTH_ERROR);
passwd = database->server->monpw; monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
} }
else
if (uname == NULL)
return;
/* Don't probe servers in maintenance mode */
if (SERVER_IN_MAINT(database->server))
return;
/** Store previous status */
database->mon_prev_status = database->server->status;
if (database->con == NULL || mysql_ping(database->con) != 0)
{ {
char *dpwd = decryptPassword(passwd); /* The current server is not running
int connect_timeout = mon->connect_timeout; *
int read_timeout = mon->read_timeout; * Store server NOT running in server and monitor server pending struct
int write_timeout = mon->write_timeout; *
*/
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
{
server_set_status(database->server, SERVER_AUTH_ERROR);
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
}
server_clear_status(database->server, SERVER_RUNNING);
monitor_clear_pending_status(database, SERVER_RUNNING);
if(database->con) /* Also clear M/S state in both server and monitor server pending struct */
mysql_close(database->con); server_clear_status(database->server, SERVER_SLAVE);
database->con = mysql_init(NULL); server_clear_status(database->server, SERVER_MASTER);
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout); /* Clean addition status too */
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout); server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER);
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout); server_clear_status(database->server, SERVER_STALE_STATUS);
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
if (mysql_real_connect(database->con, monitor_clear_pending_status(database, SERVER_STALE_STATUS);
database->server->name,
uname,
dpwd,
NULL,
database->server->port,
NULL,
0) == NULL)
{
free(dpwd);
/* The current server is not running
*
* Store server NOT running in server and monitor server pending struct
*
*/
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
{
server_set_status(database->server, SERVER_AUTH_ERROR);
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
}
server_clear_status(database->server, SERVER_RUNNING);
monitor_clear_pending_status(database, SERVER_RUNNING);
/* Also clear M/S state in both server and monitor server pending struct */ /* Log connect failure only once */
server_clear_status(database->server, SERVER_SLAVE); if (mon_status_changed(database) && mon_print_fail_status(database))
server_clear_status(database->server, SERVER_MASTER); {
monitor_clear_pending_status(database, SERVER_SLAVE); LOGIF(LE, (skygw_log_write_flush(
monitor_clear_pending_status(database, SERVER_MASTER); LOGFILE_ERROR,
"Error : Monitor was unable to connect to "
"server %s:%d : \"%s\"",
database->server->name,
database->server->port,
mysql_error(database->con))));
}
/* Clean addition status too */ return;
server_clear_status(database->server, SERVER_SLAVE_OF_EXTERNAL_MASTER);
server_clear_status(database->server, SERVER_STALE_STATUS);
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Log connect failure only once */
if (mon_status_changed(database) && mon_print_fail_status(database))
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Monitor was unable to connect to "
"server %s:%d : \"%s\"",
database->server->name,
database->server->port,
mysql_error(database->con))));
}
return;
}
else
{
server_clear_status(database->server, SERVER_AUTH_ERROR);
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
}
free(dpwd);
} }
/* Store current status in both server and monitor server pending struct */ }
server_set_status(database->server, SERVER_RUNNING); /* Store current status in both server and monitor server pending struct */
monitor_set_pending_status(database, SERVER_RUNNING); server_set_status(database->server, SERVER_RUNNING);
monitor_set_pending_status(database, SERVER_RUNNING);
/* get server version from current server */ /* get server version from current server */
server_version = mysql_get_server_version(database->con); server_version = mysql_get_server_version(database->con);
/* get server version string */ /* get server version string */
server_string = (char *)mysql_get_server_info(database->con); server_string = (char *)mysql_get_server_info(database->con);
if (server_string) { if (server_string) {
database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1); database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1);
if (database->server->server_string) if (database->server->server_string)
strcpy(database->server->server_string, server_string); strcpy(database->server->server_string, server_string);
} }
/* get server_id form current node */
if (mysql_query(database->con, "SELECT @@server_id") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
long server_id = -1;
/* get server_id form current node */ if(mysql_field_count(database->con) != 1)
if (mysql_query(database->con, "SELECT @@server_id") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
long server_id = -1;
if(mysql_field_count(database->con) != 1)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SELECT @@server_id\". Expected 1 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
server_id = strtol(row[0], NULL, 10);
if ((errno == ERANGE && (server_id == LONG_MAX
|| server_id == LONG_MIN)) || (errno != 0 && server_id == 0))
{
server_id = -1;
}
database->server->node_id = server_id;
}
mysql_free_result(result);
}
/* Check if the Slave_SQL_Running and Slave_IO_Running status is
* set to Yes
*/
/* Check first for MariaDB 10.x.x and get status for multimaster replication */
if (server_version >= 100000) {
if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
int i = 0;
long master_id = -1;
if(mysql_field_count(database->con) < 42)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" "
"returned less than the expected amount of columns. Expected 42 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
if (strncmp(row[12], "Yes", 3) == 0
&& strncmp(row[13], "Yes", 3) == 0) {
isslave += 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[12], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[41]);
if (master_id == 0)
master_id = -1;
}
i++;
}
/* store master_id of current node */
memcpy(&database->server->master_id, &master_id, sizeof(long));
mysql_free_result(result);
/* If all configured slaves are running set this node as slave */
if (isslave > 0 && isslave == i)
isslave = 1;
else
isslave = 0;
}
} else {
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
long master_id = -1;
if(mysql_field_count(database->con) < 40)
{
mysql_free_result(result);
if(server_version < 5*10000 + 5*100)
{
if(database->log_version_err)
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
" for versions less than 5.5 does not have master_server_id, "
"replication tree cannot be resolved for server %s."
" MySQL Version: %s",database->server->unique_name,version_str);
database->log_version_err = false;
}
}
else
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
"returned less than the expected amount of columns. Expected 40 columns."
" MySQL Version: %s",version_str);
}
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
if (strncmp(row[10], "Yes", 3) == 0
&& strncmp(row[11], "Yes", 3) == 0) {
isslave = 1;
}
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
* the replication tree, slaves ids will be added to master(s) and we will have at least the
* root master server.
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
*/
if (strncmp(row[10], "Yes", 3) == 0) {
/* get Master_Server_Id values */
master_id = atol(row[39]);
if (master_id == 0)
master_id = -1;
}
}
/* store master_id of current node */
memcpy(&database->server->master_id, &master_id, sizeof(long));
mysql_free_result(result);
}
}
/* Remove addition info */
monitor_clear_pending_status(database, SERVER_SLAVE_OF_EXTERNAL_MASTER);
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
/* Please note, the MASTER status and SERVER_SLAVE_OF_EXTERNAL_MASTER
* will be assigned in the monitorMain() via get_replication_tree() routine
*/
/* Set the Slave Role */
if (isslave)
{ {
monitor_set_pending_status(database, SERVER_SLAVE); mysql_free_result(result);
/* Avoid any possible stale Master state */ skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column."
monitor_clear_pending_status(database, SERVER_MASTER); " MySQL Version: %s",version_str);
} else { return;
/* Avoid any possible Master/Slave stale state */
monitor_clear_pending_status(database, SERVER_SLAVE);
monitor_clear_pending_status(database, SERVER_MASTER);
} }
while ((row = mysql_fetch_row(result)))
{
server_id = strtol(row[0], NULL, 10);
if ((errno == ERANGE && (server_id == LONG_MAX
|| server_id == LONG_MIN)) || (errno != 0 && server_id == 0))
{
server_id = -1;
}
database->server->node_id = server_id;
}
mysql_free_result(result);
}
/* Check first for MariaDB 10.x.x and get status for multi-master replication */
if (server_version >= 100000)
{
monitor_mysql100_db(database);
}
else if(server_version >= 5*10000 + 5*100)
{
monitor_mysql55_db(database);
}
else
{
monitor_mysql51_db(database);
}
} }
/** /**
@ -723,7 +866,11 @@ detect_stale_master = handle->detectStaleMaster;
} }
} else { } else {
/* Compute the replication tree */ /* Compute the replication tree */
if(handle->mysql51_replication)
root_master = build_mysql51_replication_tree(mon);
else
root_master = get_replication_tree(mon, num_servers); root_master = get_replication_tree(mon, num_servers);
} }
/* Update server status from monitor pending status on that server*/ /* Update server status from monitor pending status on that server*/
@ -1302,4 +1449,4 @@ bool isMySQLEvent(monitor_event_t event)
return true; return true;
} }
return false; return false;
} }

View File

@ -67,6 +67,7 @@ typedef struct {
int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */ int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */
int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */ int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */
int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */ int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */
bool mysql51_replication; /**< Use MySQL 5.1 replication */
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */ MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
char* script; /*< Script to call when state changes occur on servers */ char* script; /*< Script to call when state changes occur on servers */
bool events[MAX_MONITOR_EVENT]; /*< enabled events */ bool events[MAX_MONITOR_EVENT]; /*< enabled events */