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

@ -473,6 +473,8 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
dbnm)));
}
if(srvname)
{
char* old_backend = (char*)hashtable_fetch(hashtable,dbnm);
int j;
bool is_alive = false;
@ -484,8 +486,8 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
* alive. If not then update
* the hashtable with the current backend's name.
*/
if(strcmp(server->unique_name,old_backend) == 0 &&
SERVER_IS_RUNNING(server))
if(strcmp(backends[j]->backend_server->unique_name,old_backend) == 0 &&
SERVER_IS_RUNNING(backends[j]->backend_server))
{
is_alive = true;
break;
@ -511,6 +513,7 @@ bool update_dbnames_hash(BACKEND** backends, HASHTABLE* hashtable)
"Error: failed to insert values into hashtable.")));
}
}
}
} /*< hashtable_add failed */
} /*< while */
@ -558,12 +561,13 @@ void* dbnames_hash_init(BACKEND** backends)
return htbl;
}
bool add_shard_info(GWBUF* buffer, char* target)
{
HINT* hint = hint_create_route(NULL,HINT_ROUTE_TO_NAMED_SERVER,target);
return (bool)gwbuf_add_hint(buffer,hint);
}
/**
* Check the hashtable for the right backend for this query.
* @param router Router instance
* @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){
HASHTABLE* ht = router->dbnames_hash;
int sz = 0,i,j;
@ -603,6 +607,36 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
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
*
@ -930,6 +964,7 @@ static void* newSession(
client_rses->router = router;
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
* router instance first.
@ -1344,19 +1379,20 @@ static route_target_t get_shard_route_target (
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) ||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
/** Configured to allow writing variables to all nodes */
(use_sql_variables_in == TYPE_ALL &&
(use_sql_variables_in == TYPE_ALL &&
QUERY_IS_TYPE(qtype, QUERY_TYPE_GSYSVAR_WRITE)) ||
/** enable or disable autocommit are always routed to all */
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 */
target = TARGET_ALL;
}
else
{
target = TARGET_NAMED_SERVER;
}
/* else */
/* { */
/* target = TARGET_NAMED_SERVER; */
/* } */
#if defined(SS_DEBUG)
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
@ -1651,7 +1687,7 @@ static int routeQuery(
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
bool rses_is_closed = false;
route_target_t route_target;
route_target_t route_target = TARGET_UNDEFINED;
bool succp = false;
char* tname = NULL;
@ -1749,8 +1785,8 @@ static int routeQuery(
LOGFILE_ERROR,
"Error : Changing database failed.")));
}
ret = 1;
goto retblock;
/* ret = 1; */
/* goto retblock; */
}
/**
@ -1851,18 +1887,29 @@ static int routeQuery(
* 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)
{
bool shard_ok = check_shard_status(inst,tname);
if(shard_ok)
{
route_target = TARGET_NAMED_SERVER;
}
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
{
@ -1873,6 +1920,45 @@ static int routeQuery(
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))
{
/**
@ -1992,6 +2078,7 @@ retblock:
}
#endif
gwbuf_free(querybuf);
return ret;
}
@ -4176,6 +4263,9 @@ static bool change_current_db(
* 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.
*/
update_dbnames_hash(inst->servers,inst->dbnames_hash);
if(hashtable_fetch(
inst->dbnames_hash,
(char*)rses->rses_mysql_session->db) == NULL)