Added error message generation for non-existent databases and backend status checks.

This commit is contained in:
Markus Makela
2014-12-11 11:45:40 +02:00
parent 78d09c35cb
commit a7cc40dc6a
2 changed files with 118 additions and 28 deletions

View File

@ -83,7 +83,7 @@ typedef enum {
TARGET_RLAG_MAX = 0x10 TARGET_RLAG_MAX = 0x10
} route_target_t; } route_target_t;
#define TARGET_IS_UNDEFINED(t) (t == TARGET_UNDEFINED)
#define TARGET_IS_NAMED_SERVER(t) (t & TARGET_NAMED_SERVER) #define TARGET_IS_NAMED_SERVER(t) (t & TARGET_NAMED_SERVER)
#define TARGET_IS_ALL(t) (t & TARGET_ALL) #define TARGET_IS_ALL(t) (t & TARGET_ALL)
@ -249,6 +249,7 @@ struct router_client_session {
SPINLOCK rses_lock; /*< protects rses_deleted */ SPINLOCK rses_lock; /*< protects rses_deleted */
int rses_versno; /*< even = no active update, else odd. not used 4/14 */ int rses_versno; /*< even = no active update, else odd. not used 4/14 */
bool rses_closed; /*< true when closeSession is called */ bool rses_closed; /*< true when closeSession is called */
DCB* rses_client_dcb;
MYSQL_session* rses_mysql_session; MYSQL_session* rses_mysql_session;
/** Properties listed by their type */ /** Properties listed by their type */
rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT]; rses_property_t* rses_properties[RSES_PROP_TYPE_COUNT];
@ -299,7 +300,6 @@ typedef struct router_instance {
bool available_slaves; /*< The router has some slaves available */ bool available_slaves; /*< The router has some slaves available */
HASHTABLE* dbnames_hash; /** Hashtable containing the database names and where to find them */ HASHTABLE* dbnames_hash; /** Hashtable containing the database names and where to find them */
char** ignore_list; char** ignore_list;
bool update_hash;
} 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 : \

View File

@ -473,6 +473,8 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
dbnm))); dbnm)));
} }
if(srvname)
{
char* old_backend = (char*)hashtable_fetch(hashtable,dbnm); char* old_backend = (char*)hashtable_fetch(hashtable,dbnm);
int j; int j;
bool is_alive = false; bool is_alive = false;
@ -484,8 +486,8 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
* alive. If not then update * alive. If not then update
* the hashtable with the current backend's name. * the hashtable with the current backend's name.
*/ */
if(strcmp(server->unique_name,old_backend) == 0 && if(strcmp(backends[j]->backend_server->unique_name,old_backend) == 0 &&
SERVER_IS_RUNNING(server)) SERVER_IS_RUNNING(backends[j]->backend_server))
{ {
is_alive = true; is_alive = true;
break; break;
@ -511,6 +513,7 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
"Error: failed to insert values into hashtable."))); "Error: failed to insert values into hashtable.")));
} }
} }
}
} /*< hashtable_add failed */ } /*< hashtable_add failed */
} /*< while */ } /*< while */
@ -558,12 +561,13 @@ void* dbnames_hash_init(BACKEND** backends)
return htbl; return htbl;
} }
bool add_shard_info(GWBUF* buffer, char* target) /**
{ * Check the hashtable for the right backend for this query.
HINT* hint = hint_create_route(NULL,HINT_ROUTE_TO_NAMED_SERVER,target); * @param router Router instance
return (bool)gwbuf_add_hint(buffer,hint); * @param client Client router session
} * @param buffer Query to inspect
* @return Name of the backend or NULL if the query contains no known databases.
*/
char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer){ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer){
HASHTABLE* ht = router->dbnames_hash; HASHTABLE* ht = router->dbnames_hash;
int sz = 0,i,j; int sz = 0,i,j;
@ -603,6 +607,36 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
return rval; return rval;
} }
/**
* Check if the backend is still running. If the backend is not running the
* hashtable is updated with up-to-date values.
* @param router Router instance
* @param shard Shard to check
* @return True if the backend server is running
*/
bool check_shard_status(ROUTER_INSTANCE* router, char* shard)
{
int i;
bool rval = false;
for(i = 0;router->servers[i];i++)
{
if(strcmp(router->servers[i]->backend_server->unique_name,shard) == 0)
{
if(SERVER_IS_RUNNING(router->servers[i]->backend_server))
{
rval = true;
}
else
{
update_dbnames_hash(router->servers,router->dbnames_hash);
}
break;
}
}
return rval;
}
/** /**
* Implementation of the mandatory version entry point * Implementation of the mandatory version entry point
* *
@ -930,6 +964,7 @@ static void* newSession(
client_rses->router = router; client_rses->router = router;
client_rses->rses_mysql_session = (MYSQL_session*)session->data; client_rses->rses_mysql_session = (MYSQL_session*)session->data;
client_rses->rses_client_dcb = (DCB*)session->client;
/** /**
* If service config has been changed, reload config from service to * If service config has been changed, reload config from service to
* router instance first. * router instance first.
@ -1348,15 +1383,16 @@ static route_target_t get_shard_route_target (
QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE)) || QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE)) ||
/** enable or disable autocommit are always routed to all */ /** enable or disable autocommit are always routed to all */
QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) || QUERY_IS_TYPE(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)) QUERY_IS_TYPE(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) ||
QUERY_IS_TYPE(qtype, QUERY_TYPE_SYSVAR_READ)) /** added for @@version comment, temporary*/
{ {
/** hints don't affect on routing */ /** hints don't affect on routing */
target = TARGET_ALL; target = TARGET_ALL;
} }
else /* else */
{ /* { */
target = TARGET_NAMED_SERVER; /* target = TARGET_NAMED_SERVER; */
} /* } */
#if defined(SS_DEBUG) #if defined(SS_DEBUG)
LOGIF(LT, (skygw_log_write( LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE, LOGFILE_TRACE,
@ -1651,7 +1687,7 @@ static int routeQuery(
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session; ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
bool rses_is_closed = false; bool rses_is_closed = false;
route_target_t route_target; route_target_t route_target = TARGET_UNDEFINED;
bool succp = false; bool succp = false;
char* tname = NULL; char* tname = NULL;
@ -1749,8 +1785,8 @@ static int routeQuery(
LOGFILE_ERROR, LOGFILE_ERROR,
"Error : Changing database failed."))); "Error : Changing database failed.")));
} }
ret = 1; /* ret = 1; */
goto retblock; /* goto retblock; */
} }
/** /**
@ -1851,20 +1887,31 @@ static int routeQuery(
* eventually to master * eventually to master
*/ */
/**
* Update the hashtable
*/
if(inst->update_hash)
{
update_dbnames_hash(inst->servers,inst->dbnames_hash);
}
if((tname = get_shard_target_name(inst,router_cli_ses,querybuf)) != NULL) if((tname = get_shard_target_name(inst,router_cli_ses,querybuf)) != NULL)
{
bool shard_ok = check_shard_status(inst,tname);
if(shard_ok)
{ {
route_target = TARGET_NAMED_SERVER; route_target = TARGET_NAMED_SERVER;
} }
else else
{
/**
* Shard is not a viable target right now so we check
* for an alternate backend with the database. If this is not found
* the target is undefined and an error will be returned to the client.
*/
if((tname = get_shard_target_name(inst,router_cli_ses,querybuf)) != NULL &&
check_shard_status(inst,tname))
{
route_target = TARGET_NAMED_SERVER;
}
}
}
else
{ {
route_target = get_shard_route_target(qtype, route_target = get_shard_route_target(qtype,
@ -1873,6 +1920,45 @@ static int routeQuery(
querybuf->hint); querybuf->hint);
} }
if(TARGET_IS_UNDEFINED(route_target))
{
/**
* No valid targets found for this query, return an error packet and update the hashtable. This also adds new databases to the hashtable.
*/
char errstr[2048];
GWBUF *errbuff;
update_dbnames_hash(inst->servers,inst->dbnames_hash);
if(tname == NULL &&
router_cli_ses->rses_mysql_session->db[0] == '\0')
{
/**
* No current database or databases in query.
*/
errbuff = modutil_create_mysql_err_msg(1,0,1046,
"3D000",
"No database selected");
}
else
{
/**
* Bad shard status
*/
sprintf(errstr,"Unknown database '%s'",
router_cli_ses->rses_mysql_session->db);
errbuff = modutil_create_mysql_err_msg(1,0,1049,
"42000",
errstr);
}
router_cli_ses->rses_client_dcb->func.write(router_cli_ses->rses_client_dcb,errbuff);
}
if (TARGET_IS_ALL(route_target)) if (TARGET_IS_ALL(route_target))
{ {
/** /**
@ -1992,6 +2078,7 @@ retblock:
} }
#endif #endif
gwbuf_free(querybuf); gwbuf_free(querybuf);
return ret; return ret;
} }
@ -4176,6 +4263,9 @@ static bool change_current_db(
* Update the session's active database only if it's in the hashtable. * Update the session's active database only if it's in the hashtable.
* If it isn't found, send a custom error packet to the client. * If it isn't found, send a custom error packet to the client.
*/ */
update_dbnames_hash(inst->servers,inst->dbnames_hash);
if(hashtable_fetch( if(hashtable_fetch(
inst->dbnames_hash, inst->dbnames_hash,
(char*)rses->rses_mysql_session->db) == NULL) (char*)rses->rses_mysql_session->db) == NULL)