Files
MaxScale/server/modules/monitor/ndbclustermon/ndbclustermon.cc
Johan Wikman 44fa2a4be2 MXS-1848 Change prototype of startMonitor
StartMonitor() now takes a MXS_MONITOR_INSTANCE and returns
true, if the monitor could be started and false otherwise.
So, the setup is such that in createInstance(), the instance
data is created and then using startMonitor() and stopMonitor()
the monitor is started/stopped. Finally in destroyInstance(),
the actual instance data is deleted.
2018-05-07 14:08:36 +03:00

418 lines
11 KiB
C++

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
/**
* @file ndbcluster_mon.c - A MySQL cluster SQL node monitor
*/
#define MXS_MODULE_NAME "ndbclustermon"
#include "ndbclustermon.h"
#include <maxscale/alloc.h>
#include <maxscale/mysql_utils.h>
static void monitorMain(void *);
/* @see function load_module in load_utils.c for explanation of the following
* lint directives.
*/
/*lint -e14 */
/*lint +e14 */
static MXS_MONITOR_INSTANCE *createInstance(MXS_MONITOR *);
static void destroyInstance(MXS_MONITOR_INSTANCE*);
static bool startMonitor(MXS_MONITOR_INSTANCE *, const MXS_CONFIG_PARAMETER *params);
static void stopMonitor(MXS_MONITOR_INSTANCE *);
static void diagnostics(const MXS_MONITOR_INSTANCE *, DCB *);
static json_t* diagnostics_json(const MXS_MONITOR_INSTANCE *);
bool isNdbEvent(mxs_monitor_event_t event);
extern "C"
{
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
* "module object", this is a structure with the set of
* external entry points for this module.
*
* @return The module object
*/
MXS_MODULE* MXS_CREATE_MODULE()
{
MXS_NOTICE("Initialise the MySQL Cluster Monitor module.");
static MXS_MONITOR_API MyObject =
{
createInstance,
destroyInstance,
startMonitor,
stopMonitor,
diagnostics,
diagnostics_json
};
static MXS_MODULE info =
{
MXS_MODULE_API_MONITOR,
MXS_MODULE_BETA_RELEASE,
MXS_MONITOR_VERSION,
"A MySQL cluster SQL node monitor",
"V2.1.0",
MXS_NO_MODULE_CAPABILITIES,
&MyObject,
NULL, /* Process init. */
NULL, /* Process finish. */
NULL, /* Thread init. */
NULL, /* Thread finish. */
{
{
"script",
MXS_MODULE_PARAM_PATH,
NULL,
MXS_MODULE_OPT_PATH_X_OK
},
{
"events",
MXS_MODULE_PARAM_ENUM,
MXS_MONITOR_EVENT_DEFAULT_VALUE,
MXS_MODULE_OPT_NONE,
mxs_monitor_event_enum_values
},
{MXS_END_MODULE_PARAMS} // No parameters
}
};
return &info;
}
}
/*lint +e14 */
static MXS_MONITOR_INSTANCE* createInstance(MXS_MONITOR *mon)
{
NDBC_MONITOR* handle = static_cast<NDBC_MONITOR*>(MXS_CALLOC(1, sizeof(NDBC_MONITOR)));
if (handle)
{
handle->shutdown = 0;
handle->id = MXS_MONITOR_DEFAULT_ID;
handle->master = NULL;
handle->monitor = mon;
handle->script = NULL;
handle->events = 0;
handle->checked = false;
}
return handle;
}
void destroyInstance(MXS_MONITOR_INSTANCE* mon)
{
NDBC_MONITOR* handle = static_cast<NDBC_MONITOR*>(mon);
ss_dassert(!handle->thread);
ss_dassert(!handle->script);
MXS_FREE(handle);
}
/**
* Start the instance of the monitor, returning a handle on the monitor.
*
* This function creates a thread to execute the actual monitoring.
*
* @return A handle to use when interacting with the monitor
*/
static bool startMonitor(MXS_MONITOR_INSTANCE *mon, const MXS_CONFIG_PARAMETER *params)
{
bool started = false;
NDBC_MONITOR *handle = static_cast<NDBC_MONITOR*>(mon);
if (!handle->checked)
{
if (!check_monitor_permissions(handle->monitor, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'"))
{
MXS_ERROR("Failed to start monitor. See earlier errors for more information.");
}
else
{
handle->checked = true;
}
}
if (handle->checked)
{
handle->script = config_copy_string(params, "script");
handle->events = config_get_enum(params, "events", mxs_monitor_event_enum_values);
if (thread_start(&handle->thread, monitorMain, handle, 0) == NULL)
{
MXS_ERROR("Failed to start monitor thread for monitor '%s'.", handle->monitor->name);
MXS_FREE(handle->script);
handle->script = NULL;
}
else
{
started = true;
}
}
return started;
}
/**
* Stop a running monitor
*
* @param arg Handle on thr running monior
*/
static void
stopMonitor(MXS_MONITOR_INSTANCE *mon)
{
NDBC_MONITOR *handle = static_cast<NDBC_MONITOR*>(mon);
ss_dassert(handle->thread);
handle->shutdown = 1;
thread_wait(handle->thread);
handle->thread = 0;
handle->shutdown = 0;
MXS_FREE(handle->script);
handle->script = NULL;
}
/**
* Diagnostic interface
*
* @param dcb DCB to send output
* @param arg The monitor handle
*/
static void
diagnostics(const MXS_MONITOR_INSTANCE *mon, DCB *dcb)
{
}
/**
* Diagnostic interface
*
* @param dcb DCB to send output
* @param arg The monitor handle
*/
static json_t* diagnostics_json(const MXS_MONITOR_INSTANCE *mon)
{
return NULL;
}
/**
* Monitor an individual server
*
* @param database The database to probe
*/
static void
monitorDatabase(MXS_MONITORED_SERVER *database, char *defaultUser, char *defaultPasswd, MXS_MONITOR *mon)
{
MYSQL_ROW row;
MYSQL_RES *result;
int isjoined = 0;
char *server_string;
/* Don't even probe server flagged as in maintenance */
if (SERVER_IN_MAINT(database->server))
{
return;
}
mxs_connect_result_t rval = mon_ping_or_connect_to_db(mon, database);
if (rval != MONITOR_CONN_OK)
{
server_clear_status_nolock(database->server, SERVER_RUNNING);
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
{
server_set_status_nolock(database->server, SERVER_AUTH_ERROR);
}
database->server->node_id = -1;
if (mon_status_changed(database) && mon_print_fail_status(database))
{
mon_log_connect_error(database, rval);
}
return;
}
server_clear_status_nolock(database->server, SERVER_AUTH_ERROR);
/* If we get this far then we have a working connection */
server_set_status_nolock(database->server, SERVER_RUNNING);
/* get server version string */
mxs_mysql_set_server_version(database->con, database->server);
server_string = database->server->version_string;
/* Check if the the SQL node is able to contact one or more data nodes */
if (mxs_mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if (mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
MXS_ERROR("Unexpected result for \"SHOW STATUS LIKE "
"'Ndb_number_of_ready_data_nodes'\". Expected 2 columns."
" MySQL Version: %s", server_string);
return;
}
while ((row = mysql_fetch_row(result)))
{
if (atoi(row[1]) > 0)
{
isjoined = 1;
}
}
mysql_free_result(result);
}
else
{
mon_report_query_error(database);
}
/* Check the the SQL node id in the MySQL cluster */
if (mxs_mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if (mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
MXS_ERROR("Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". "
"Expected 2 columns."
" MySQL Version: %s", server_string);
return;
}
long cluster_node_id = -1;
while ((row = mysql_fetch_row(result)))
{
cluster_node_id = strtol(row[1], NULL, 10);
if ((errno == ERANGE && (cluster_node_id == LONG_MAX
|| cluster_node_id == LONG_MIN)) || (errno != 0 && cluster_node_id == 0))
{
cluster_node_id = -1;
}
database->server->node_id = cluster_node_id;
}
mysql_free_result(result);
}
else
{
mon_report_query_error(database);
}
if (isjoined)
{
server_set_status_nolock(database->server, SERVER_NDB);
database->server->depth = 0;
}
else
{
server_clear_status_nolock(database->server, SERVER_NDB);
database->server->depth = -1;
}
}
/**
* The entry point for the monitoring module thread
*
* @param arg The handle of the monitor
*/
static void
monitorMain(void *arg)
{
NDBC_MONITOR *handle = (NDBC_MONITOR*)arg;
MXS_MONITOR* mon = handle->monitor;
MXS_MONITORED_SERVER *ptr;
size_t nrounds = 0;
if (mysql_thread_init())
{
MXS_ERROR("Fatal : mysql_thread_init failed in monitor module. Exiting.");
return;
}
handle->status = MXS_MONITOR_RUNNING;
load_server_journal(mon, NULL);
while (1)
{
if (handle->shutdown)
{
handle->status = MXS_MONITOR_STOPPING;
mysql_thread_end();
handle->status = MXS_MONITOR_STOPPED;
return;
}
/** Wait base interval */
thread_millisleep(MXS_MON_BASE_INTERVAL_MS);
/**
* Calculate how far away the monitor interval is from its full
* cycle and if monitor interval time further than the base
* interval, then skip monitoring checks. Excluding the first
* round.
*/
if (nrounds != 0 &&
((nrounds * MXS_MON_BASE_INTERVAL_MS) % mon->interval) >=
MXS_MON_BASE_INTERVAL_MS)
{
nrounds += 1;
continue;
}
nrounds += 1;
lock_monitor_servers(mon);
servers_status_pending_to_current(mon);
ptr = mon->monitored_servers;
while (ptr)
{
ptr->mon_prev_status = ptr->server->status;
monitorDatabase(ptr, mon->user, mon->password, mon);
if (ptr->server->status != ptr->mon_prev_status ||
SERVER_IS_DOWN(ptr->server))
{
MXS_DEBUG("Backend server [%s]:%d state : %s",
ptr->server->address,
ptr->server->port,
STRSRVSTATUS(ptr->server));
}
ptr = ptr->next;
}
/**
* After updating the status of all servers, check if monitor events
* need to be launched.
*/
mon_process_state_changes(mon, handle->script, handle->events);
mon_hangup_failed_servers(mon);
servers_status_current_to_pending(mon);
store_server_journal(mon, NULL);
release_monitor_servers(mon);
}
}