Merge branch 'MAX-324' of https://github.com/mariadb-corporation/MaxScale into MAX-324
Conflicts: server/modules/include/dbshard.h server/modules/routing/dbshard/dbshard.c
This commit is contained in:
@ -1,5 +1,28 @@
|
|||||||
#ifndef _DBSHARDROUTER_H
|
#ifndef _DBSHARDROUTER_H
|
||||||
#define _DBSHARDROUTER_H
|
#define _DBSHARDROUTER_H
|
||||||
|
/*
|
||||||
|
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||||
|
* software: you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation,
|
||||||
|
* version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Copyright MariaDB Corporation Ab 2013-2014
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file router.h - The dbshard router module heder file
|
||||||
|
*
|
||||||
|
* @verbatim
|
||||||
|
* Revision History
|
||||||
*
|
*
|
||||||
* See GitHub https://github.com/skysql/MaxScale
|
* See GitHub https://github.com/skysql/MaxScale
|
||||||
*
|
*
|
||||||
@ -293,9 +316,14 @@ typedef struct router_instance {
|
|||||||
unsigned int bitvalue; /*< Required value of server->status */
|
unsigned int bitvalue; /*< Required value of server->status */
|
||||||
ROUTER_STATS stats; /*< Statistics for this router */
|
ROUTER_STATS stats; /*< Statistics for this router */
|
||||||
struct router_instance* next; /*< Next router on the list */
|
struct router_instance* next; /*< Next router on the list */
|
||||||
|
bool available_slaves; /*< The router has some slaves available */
|
||||||
|
HASHTABLE* dbnames_hash; /** Hashtable containing the database names and where to find them */
|
||||||
} ROUTER_INSTANCE;
|
} ROUTER_INSTANCE;
|
||||||
|
|
||||||
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
|
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
|
||||||
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED));
|
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED));
|
||||||
|
|
||||||
|
void* dbnames_hash_init(BACKEND** backends);
|
||||||
|
bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable);
|
||||||
|
|
||||||
#endif /*< _DBSHARDROUTER_H */
|
#endif /*< _DBSHARDROUTER_H */
|
||||||
|
|||||||
@ -6,6 +6,10 @@ add_library(testroute SHARED testroute.c)
|
|||||||
target_link_libraries(testroute log_manager utils)
|
target_link_libraries(testroute log_manager utils)
|
||||||
install(TARGETS testroute DESTINATION modules)
|
install(TARGETS testroute DESTINATION modules)
|
||||||
|
|
||||||
|
add_library(dbshard SHARED dbshard/dbshard.c)
|
||||||
|
target_link_libraries(dbshard log_manager utils query_classifier)
|
||||||
|
install(TARGETS dbshard DESTINATION modules)
|
||||||
|
|
||||||
add_library(readconnroute SHARED readconnroute.c)
|
add_library(readconnroute SHARED readconnroute.c)
|
||||||
target_link_libraries(readconnroute log_manager utils)
|
target_link_libraries(readconnroute log_manager utils)
|
||||||
install(TARGETS readconnroute DESTINATION modules)
|
install(TARGETS readconnroute DESTINATION modules)
|
||||||
|
|||||||
@ -233,6 +233,11 @@ static void tracelog_routed_query(
|
|||||||
backend_ref_t* bref,
|
backend_ref_t* bref,
|
||||||
GWBUF* buf);
|
GWBUF* buf);
|
||||||
|
|
||||||
|
static void dbshard_process_router_options(
|
||||||
|
ROUTER_INSTANCE* router,
|
||||||
|
char** options);
|
||||||
|
|
||||||
|
|
||||||
static bool route_session_write(
|
static bool route_session_write(
|
||||||
ROUTER_CLIENT_SES* router_client_ses,
|
ROUTER_CLIENT_SES* router_client_ses,
|
||||||
GWBUF* querybuf,
|
GWBUF* querybuf,
|
||||||
@ -311,6 +316,153 @@ static void* hfree(void* fval)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the hashtable with the database names and where to find them, adding new and removing obsolete pairs.
|
||||||
|
* @param backends Backends to query for database names
|
||||||
|
* @param hashtable Hashtable to use
|
||||||
|
* @return True if all database and server names were successfully retrieved otherwise false
|
||||||
|
*/
|
||||||
|
bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
|
||||||
|
{
|
||||||
|
const unsigned int connect_timeout = 5;
|
||||||
|
const unsigned int read_timeout = 2;
|
||||||
|
bool rval = true;
|
||||||
|
SERVER* server;
|
||||||
|
MYSQL* handle;
|
||||||
|
MYSQL_RES* result;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
int i, rc, numfields;
|
||||||
|
|
||||||
|
for(i = 0;backends[i] && rval;i++){
|
||||||
|
|
||||||
|
handle = mysql_init(NULL);
|
||||||
|
|
||||||
|
if(handle == NULL){
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to initialize MySQL handle.")));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
rc |= mysql_options(handle, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||||
|
rc |= mysql_options(handle, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||||
|
if(rc != 0){
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set MySQL connection options.")));
|
||||||
|
mysql_close(handle);
|
||||||
|
rval = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = backends[i]->backend_server;
|
||||||
|
ss_dassert(server != NULL);
|
||||||
|
|
||||||
|
if (mysql_real_connect(handle,
|
||||||
|
server->name,
|
||||||
|
server->monuser,
|
||||||
|
server->monpw,
|
||||||
|
NULL,
|
||||||
|
server->port,
|
||||||
|
NULL,
|
||||||
|
0) == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(
|
||||||
|
LOGFILE_ERROR,
|
||||||
|
"Error: Failed to connect to backend server '%s'.",server->name)));
|
||||||
|
rval = false;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server was successfully connected to, proceed to query for database names
|
||||||
|
*/
|
||||||
|
|
||||||
|
if((result = mysql_list_dbs(handle,NULL)) == NULL){
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||||
|
"Error: Failed to execute query in backend server '%s'.",server->name)));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
numfields = mysql_num_fields(result);
|
||||||
|
|
||||||
|
if(numfields < 1){
|
||||||
|
LOGIF(LT, (skygw_log_write_flush(LOGFILE_TRACE,
|
||||||
|
"Backend '%s' has no databases.",server->name)));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walk through the list of databases in this backend
|
||||||
|
* and insert them into the hashtable. If the value is already in the hashtable
|
||||||
|
* but the backend isn't in the list of backends it is replaced with the first found backend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
while((row = mysql_fetch_row(result)))
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned long *lengths;
|
||||||
|
char *dbnm = NULL,*servnm = NULL;
|
||||||
|
|
||||||
|
lengths = mysql_fetch_lengths(result);
|
||||||
|
dbnm = (char*)calloc(lengths[0] + 1,sizeof(char));
|
||||||
|
memcpy(dbnm,row[0],lengths[0]);
|
||||||
|
servnm = strdup(server->unique_name);
|
||||||
|
|
||||||
|
if(hashtable_add(hashtable,dbnm,servnm) == 0){
|
||||||
|
|
||||||
|
/*Check if the failure was due to a duplicate value*/
|
||||||
|
if(hashtable_fetch(hashtable,dbnm) == NULL){
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||||
|
"Error: Failed to insert values into hashtable.")));
|
||||||
|
}else{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||||
|
"Error: Duplicate value found.")));
|
||||||
|
}
|
||||||
|
rval = false;
|
||||||
|
free(dbnm);
|
||||||
|
free(servnm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
if(result){
|
||||||
|
mysql_free_result(result);
|
||||||
|
}
|
||||||
|
result = NULL;
|
||||||
|
mysql_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates a new hashtable and inserts database names and where to find them into it.
|
||||||
|
* @param backends Backends to query for database names
|
||||||
|
* @return Pointer to the newly allocated hashtable or NULL if an error occurred
|
||||||
|
*/
|
||||||
|
void* dbnames_hash_init(BACKEND** backends)
|
||||||
|
{
|
||||||
|
HASHTABLE* htbl = hashtable_alloc(32,hashkeyfun,hashcmpfun);
|
||||||
|
|
||||||
|
if(htbl == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Hashtable allocation failed.")));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**Update the new hashtable with the key-value pairs*/
|
||||||
|
if(!update_dbnames_hash(backends,htbl)){
|
||||||
|
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Errors encountered while querying databases.")));
|
||||||
|
hashtable_free(htbl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return htbl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the mandatory version entry point
|
* Implementation of the mandatory version entry point
|
||||||
*
|
*
|
||||||
@ -574,11 +726,11 @@ createInstance(SERVICE *service, char **options)
|
|||||||
* is used if any.
|
* is used if any.
|
||||||
*/
|
*/
|
||||||
router->dbshard_version = service->svc_config_version;
|
router->dbshard_version = service->svc_config_version;
|
||||||
|
refreshInstance(router, NULL);
|
||||||
/**
|
/**
|
||||||
* Get hashtable which includes dbname,backend pairs
|
* Get hashtable which includes dbname,backend pairs
|
||||||
*/
|
*/
|
||||||
router->dbnames_hash = dbnames_hash_init(router->servers);
|
router->dbnames_hash = (HASHTABLE*)dbnames_hash_init(router->servers);
|
||||||
/**
|
/**
|
||||||
* We have completed the creation of the router data, so now
|
* We have completed the creation of the router data, so now
|
||||||
* insert this router into the linked list of routers
|
* insert this router into the linked list of routers
|
||||||
@ -4252,11 +4404,54 @@ static backend_ref_t* get_root_master_bref(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dbshard_process_router_options(
|
||||||
|
ROUTER_INSTANCE* router,
|
||||||
|
char** options)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char* value;
|
||||||
|
select_criteria_t c;
|
||||||
|
|
||||||
|
for (i = 0; options[i]; i++)
|
||||||
|
{
|
||||||
|
if ((value = strchr(options[i], '=')) == NULL)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write(
|
||||||
|
LOGFILE_ERROR, "Warning : Unsupported "
|
||||||
|
"router option \"%s\" for "
|
||||||
|
"readwritesplit router.",
|
||||||
|
options[i])));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*value = 0;
|
||||||
|
value++;
|
||||||
|
if (strcmp(options[i], "slave_selection_criteria") == 0)
|
||||||
|
{
|
||||||
|
c = GET_SELECT_CRITERIA(value);
|
||||||
|
ss_dassert(
|
||||||
|
c == LEAST_GLOBAL_CONNECTIONS ||
|
||||||
|
c == LEAST_ROUTER_CONNECTIONS ||
|
||||||
|
c == LEAST_BEHIND_MASTER ||
|
||||||
|
c == LEAST_CURRENT_OPERATIONS ||
|
||||||
|
c == UNDEFINED_CRITERIA);
|
||||||
|
|
||||||
|
if (c == UNDEFINED_CRITERIA)
|
||||||
|
{
|
||||||
|
LOGIF(LE, (skygw_log_write(
|
||||||
|
LOGFILE_ERROR, "Warning : Unknown "
|
||||||
|
"slave selection criteria \"%s\". "
|
||||||
|
"Allowed values are LEAST_GLOBAL_CONNECTIONS, "
|
||||||
|
"LEAST_ROUTER_CONNECTIONS, "
|
||||||
|
"LEAST_BEHIND_MASTER,"
|
||||||
|
"and LEAST_CURRENT_OPERATIONS.",
|
||||||
|
STRCRITERIA(router->rwsplit_config.rw_slave_select_criteria))));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
router->rwsplit_config.rw_slave_select_criteria = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /*< for */
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user